diff --git a/.mintlify/Assistant.md b/.mintlify/Assistant.md index 0353c57d..fea131b2 100644 --- a/.mintlify/Assistant.md +++ b/.mintlify/Assistant.md @@ -91,8 +91,8 @@ These are the areas where answers go wrong most often. The facts are grouped by - **Volume Set `fileSystemType` AND `performanceClass` are both immutable** after creation. `general-purpose-ssd` requires min 10 GB; `high-throughput-ssd` requires **min 200 GB**. - **Snapshots, `shrinkVolume`, `deleteVolume`, `restoreVolume`, and custom encryption are NOT supported on `shared` filesystem** — only `ext4` and `xfs`. -- **Volume shrink (`cpln volumeset shrink`) destroys data** — it provisions a new smaller volume and permanently deletes the old one. Only safe for distributed systems with data replication. -- **Volumes can only be expanded once every 6 hours.** +- **Volume shrink (`cpln volumeset shrink`) keeps the data**: it copies it to a new smaller volume and keeps the old one if the copy fails. It is refused below the volume set's `initialCapacity`, below the used bytes plus 5%, or when the volume has no current usage metric. +- **A volume can be expanded at most 4 times in a rolling 24 hours.** ### Images @@ -231,7 +231,7 @@ These are the areas where answers go wrong most often. The facts are grouped by | `shared` (auto-set when fileSystemType is shared) | 10 GB | 65,536 GB | - **Both `fileSystemType` AND `performanceClass` are immutable** after creation. -- Volumes can only be expanded once every 6 hours. +- A volume can be expanded at most 4 times in a rolling 24 hours. - Mount in workload via `cpln://volumeset/VOLUMESET_NAME` with `recoveryPolicy: retain` (default) or `recycle`. ## Resource scoping diff --git a/AGENTS.md b/AGENTS.md index 5c1c2216..bd0266dd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -234,6 +234,6 @@ These are the most common sources of documentation errors: 10. **Images** must be `linux/amd64` 11. **Orgs are immutable** — once created, an org cannot be deleted 12. **`cpln workload log` does NOT exist** — use `cpln logs '{gvc="GVC", workload="WORKLOAD"}'` with LogQL syntax -13. **Volume shrink destroys data** — `shrinkVolume` provisions a NEW smaller volume; existing data is NOT migrated. Only use for systems with data replication (Kafka, CockroachDB) +13. **Volume shrink keeps the data**: `shrinkVolume` copies it to a NEW smaller volume and keeps the old one if the copy fails. It is refused below the volume set's `initialCapacity`, below the used bytes plus 5%, or when the volume has no current usage metric 14. **Don't set `spec.identityLink` unless needed** — only assign an identity when the workload needs secret access, credential-free cloud access, or private network access (via agents) 15. **Never use `docker.io/` prefix for external images** — use the exact image reference as-is (e.g., `nginx:latest`, not `docker.io/library/nginx:latest`). The registry hostname `.registry.cpln.io` is only for `docker login`, never in workload specs diff --git a/ai/chatgpt.mdx b/ai/chatgpt.mdx index ea96431c..93070d81 100644 --- a/ai/chatgpt.mdx +++ b/ai/chatgpt.mdx @@ -55,6 +55,10 @@ Control Plane is a listed plugin in the ChatGPT Plugin directory, so there is no The listing is fixed on the default **`core`** toolset profile and takes no query parameter of its own. For `mk8s` or `full`, register the MCP server directly in [Codex](/ai/codex) or another [MCP client](/ai/mcp). See [toolset profiles](/ai/mcp#toolset-profiles). + + In ChatGPT the assistant works through the plugin's tools and never runs the `cpln` CLI. For the few steps only the CLI does, such as forwarding a port or opening a shell in a container, it gives you the command to run on your own machine. + + For the latest directory behavior, see OpenAI's [Plugins documentation](https://learn.chatgpt.com/docs/plugins). diff --git a/ai/examples.mdx b/ai/examples.mdx index 62316f5d..ab5098e1 100644 --- a/ai/examples.mdx +++ b/ai/examples.mdx @@ -219,7 +219,7 @@ and give the workload access to that secret. ### Restart, Roll Back, Promote, or Open a Workload -Each of these is one call: the assistant makes the change, waits for the workload to be ready, and reports the result. +Each of these is one call: the assistant makes the change, waits for the workload to be ready, and reports the result. Promoting needs the `full` profile. ```text Restart @@ -242,7 +242,7 @@ Let workload "web" call workload "api-server" inside GVC "production", and tell ### Run a Cron Job Now or Replace a Stuck Replica -Both have side effects in production, so the assistant confirms before running them. +Both have side effects in production, so the assistant confirms before running them. Replacing a replica needs the `full` profile. ```text Run a Cron Job Now @@ -313,6 +313,10 @@ Show logs for the "_accesslog" container of workload "web-frontend" in the last Get logs for workload "data-processor" from yesterday between 10:00 and 11:00 UTC. ``` +```text mk8s Cluster +Show the kubelet logs of mk8s cluster "edge-cluster" from the last hour. +``` + @@ -337,6 +341,10 @@ Find the slowest requests to "api-server" in the last hour and show me where the time went. ``` +```text mk8s Cluster +Which nodes of mk8s cluster "edge-cluster" used the most CPU over the last day? +``` + @@ -345,6 +353,8 @@ the time went. ### Check the Audit Trail and Quotas +The quota check needs the `full` or `readonly` profile. + ```text Recent Changes Who changed workload "api-server" in GVC "production" in the last 24 hours, and when? @@ -488,7 +498,7 @@ the link where I create its key. - A workload needs `reveal` to read a secret's values; `use` only attaches a secret to a GVC or workload. For a workload, `grant_workload_secret_access` sets the identity and the policy in one call. The service account key prompt needs the `full` profile, and the key is shown once, in the Console, never in the chat. + A workload needs `reveal` to read a secret's values; `use` only attaches a secret to a GVC or workload. For a workload, `grant_workload_secret_access` sets the identity and the policy in one call. The policy and service account prompts need the `full` profile, and the key is shown once, in the Console, never in the chat. --- @@ -617,6 +627,8 @@ Deploy a complete application stack to Control Plane: ### Environment Promotion +This needs the `full` profile. + ```text Promote workloads "frontend" and "backend" from GVC "staging" to GVC "production". Show me what differs in production first, then promote them diff --git a/ai/mcp.mdx b/ai/mcp.mdx index 4adc92a0..5582ba3f 100644 --- a/ai/mcp.mdx +++ b/ai/mcp.mdx @@ -55,9 +55,9 @@ The full tool catalog is larger than most AI tools can hold in context at once | Profile | What it includes | |---------|------------------| | `readonly` | Investigation only. Metrics, logs, traces, workload events, replicas and deployments, one-call workload diagnosis, quotas, the audit trail, configuration inventory, and Terraform export, plus a generic read tool that works on **every** resource kind. It advertises no tool that creates, updates, deletes or executes a command, so an assistant on this profile cannot change a live resource. | -| `core` *(default)* | The everyday deploy-and-operate journey. [Job tools](/ai/tools#jobs) do a whole task in one call: deploy an app, add a database, create or rotate a secret without its value passing through the chat, and diagnose, restart, roll back, promote, or open a workload to other workloads. Around them: GVCs, workloads, deployments, logs, image builds (from a repository, or from app files the assistant writes itself, so a chat can take an app from the user's request to a running URL), custom domains, identities, policies, the template catalog, volume sets, Terraform export, metrics, traces, and the audit trail, plus a generic read/delete tool that works on **every** resource kind. | +| `core` *(default)* | The everyday deploy-and-operate journey. [Job tools](/ai/tools#jobs) do a whole task in one call: deploy an app, add a database, create or rotate a secret without its value passing through the chat, and diagnose, restart, roll back, or open a workload to other workloads. Around them: GVCs, workloads, deployments, logs, image builds (from a repository, or from app files the assistant writes itself, so a chat can take an app from the user's request to a running URL), custom domains, the template catalog, volume sets with their snapshots and resizing, Terraform export, metrics, traces, the audit trail, and quota increase requests, plus a generic read/delete tool that works on **every** resource kind. | | `mk8s` | Everything in `core`, **plus** `create_mk8s` and `update_mk8s`, which create and update managed Kubernetes (mk8s) clusters on every supported provider. | -| `full` | Everything in `mk8s`, **plus** the full administrative and fine-tuning surface: cloud-account registration (AWS, GCP, Azure, NATS), org IAM (users, groups, service accounts), network-bridge agents, identity network-resource bindings, IP sets, audit contexts, external logging, advanced workload tuning (load balancer, sidecar, rollout, security, retry), domain CORS, volume-set snapshots and shrinking, GVC location edits, and org settings. | +| `full` | Everything in `mk8s`, **plus** the full administrative and fine-tuning surface: identities and policies, permission lookups, workload promotion, standalone volume set creation, quota and event reads, replica and command lists, replica stops, docs filesystem queries, cloud-account registration (AWS, GCP, Azure, NATS), org IAM (users, groups, service accounts), network-bridge agents, identity network-resource bindings, IP sets, audit contexts, external logging, advanced workload tuning (load balancer, sidecar, rollout, security, retry), and org settings. | ```text Examples https://mcp.cpln.io/mcp # core (default) diff --git a/ai/plugin.mdx b/ai/plugin.mdx index e2edd585..ba673ba2 100644 --- a/ai/plugin.mdx +++ b/ai/plugin.mdx @@ -41,15 +41,7 @@ keywords: ['claude code', 'codex', 'antigravity cli', 'cpln plugin', 'install pl Start Codex, open `/plugins`, navigate to **Control Plane**, then install the `cpln` plugin. - **Enable plugin hooks (recommended).** Codex ships with `plugin_hooks` off by default. Enable it in `~/.codex/config.toml` so the plugin's `SessionStart` hook can load the Control Plane core rules at the start of each session: - - ```toml - [features] - plugins = true - plugin_hooks = true - ``` - - After restart, `/plugins` → **Control Plane** → **Hooks** should show `SessionStart`. + **Trust the plugin's hook (recommended).** Codex runs a plugin's hooks only after you trust them. When Codex starts with a new or changed hook, it shows **Hooks need review**: choose **Review hooks** and trust the Control Plane `SessionStart` hook, or trust it later from `/hooks`. Once trusted, the hook loads the Control Plane core rules at the start of each session. diff --git a/ai/tools.mdx b/ai/tools.mdx index b7a09083..1fb07c25 100644 --- a/ai/tools.mdx +++ b/ai/tools.mdx @@ -6,11 +6,7 @@ description: Every tool the Control Plane MCP Server exposes, grouped by resourc keywords: ['mcp tools', 'tool list', 'tool reference', 'toolsets', 'readonly profile', 'core profile', 'mk8s profile', 'full profile', 'read only', 'destructive', 'cpln mcp', 'job tools'] --- -This page lists every tool the [MCP Server](/ai/mcp) exposes, grouped by the resource it operates on. Tool names are the exact identifiers the AI calls — you normally describe what you want in natural language and let the assistant pick the tool, but knowing what exists helps you prompt precisely and understand what an assistant is doing on your behalf. - - - You don't call these tools directly. Describe the outcome you want — see [Usage Examples](/ai/examples) for prompt patterns — and the assistant selects and chains the right tools. - +This page lists every tool the [MCP Server](/ai/mcp) exposes, grouped by the resource it operates on. You don't call these tools yourself: describe the outcome you want (see [Usage Examples](/ai/examples)) and the assistant picks the tools. Knowing what exists helps you prompt precisely and follow what the assistant does on your behalf. ## Toolset profiles @@ -18,10 +14,10 @@ The server advertises a different set of tools depending on the `toolsets` query | Profile | Tools | Endpoint | Includes | | :------ | :---: | :------- | :------- | -| `readonly` | 34 | `https://mcp.cpln.io/mcp?toolsets=readonly` | Investigation only: observability, runtime state, inventory, generic reads, and one-call workload diagnosis. Nothing that creates, updates, deletes, or executes, so an assistant on this profile cannot change a live resource. | -| `core` | 71 | `https://mcp.cpln.io/mcp` | The default. [Job tools](#jobs) that do a whole task in one call (deploy an app, add a database, create or rotate a secret, and diagnose, restart, roll back, or promote a workload), plus workloads, GVCs, image builds (from a repository, or from source the assistant writes itself), domains, identities, policies, templates, volume sets, observability (logs, metrics, traces, audit), quotas, and Terraform export. | -| `mk8s` | 73 | `https://mcp.cpln.io/mcp?toolsets=mk8s` | Everything in `core` plus `create_mk8s` and `update_mk8s`, which provision and update [managed Kubernetes](/mk8s/overview) clusters on every supported provider. | -| `full` | 119 | `https://mcp.cpln.io/mcp?toolsets=full` | Every tool, including agents, cloud accounts, IP sets, service accounts, groups, audit contexts, external logging, advanced workload configuration, and volume snapshots. | +| `readonly` | 27 | `https://mcp.cpln.io/mcp?toolsets=readonly` | Investigation only: observability, runtime state, inventory, generic reads, and one-call workload diagnosis. Nothing that creates, updates, deletes, or executes, so an assistant on this profile cannot change a live resource. | +| `core` | 45 | `https://mcp.cpln.io/mcp` | The default. [Job tools](#jobs) that do a whole task in one call (deploy an app, add a database, create or rotate a secret, and diagnose, restart, or roll back a workload), plus workloads, GVCs, image builds (from a repository, or from source the assistant writes itself), domains, templates, volume sets with their snapshots and resizing, observability (logs, metrics, traces, audit), quota increase requests, and Terraform export. | +| `mk8s` | 47 | `https://mcp.cpln.io/mcp?toolsets=mk8s` | Everything in `core` plus `create_mk8s` and `update_mk8s`, which provision and update [managed Kubernetes](/mk8s/overview) clusters on every supported provider. | +| `full` | 95 | `https://mcp.cpln.io/mcp?toolsets=full` | Every tool, including identities, policies, workload promotion, quota and event reads, replica stops, agents, cloud accounts, IP sets, service accounts, groups, audit contexts, external logging, and advanced workload configuration. | Connecting to `https://mcp.cpln.io/mcp` with no query parameter gives you the `core` profile. To switch profiles, point your client at the URL with the `?toolsets=` parameter — see your tool's [setup page](/ai/mcp#compatible-tools) for where the endpoint URL is configured. @@ -35,54 +31,52 @@ Each tool is listed with its name, its label, and what it does. The **Access** c | :----- | :------ | | **Read** | Read-only. Fetches data without changing anything. | | **Write** | Creates or modifies a resource. | -| **Destructive** | Deletes, removes, or overwrites data. Assistants present the impact and ask for your confirmation before running these. | +| **Destructive** | Deletes, removes, or overwrites data. The server instructs the assistant to tell you the impact and get your approval before running these. | -Tools available only in a larger profile are marked **`mk8s`** or **`full`** next to the access marker; everything else is in `core`. +Tools available only in a larger profile are marked **`mk8s`** or **`full`** next to the access marker; everything else is in `core`. The `readonly` profile has every **Read** tool except `plan_app`, `browse_templates`, `create_agent`, and `how_to_create_cloud_account`. --- ## Docs & guidance -Tools the assistant uses to ground itself in accurate Control Plane facts, schemas, and documentation. The two documentation tools, `search_control_plane` and `query_docs_filesystem_control_plane`, are proxied from the standalone docs MCP at `docs.controlplane.com/mcp`; the rest are served by the MCP Server itself. +Tools the assistant uses to ground itself in accurate Control Plane facts and documentation. The two documentation tools, `search_control_plane` and `query_docs_filesystem_control_plane`, are proxied from the standalone docs MCP at `docs.controlplane.com/mcp`; the rest are served by the MCP Server itself. | Name | Label | Description | Access | | :--- | :---- | :---------- | :----- | -| `get_cpln_rules` | Get Control Plane Operating Guide | The operating guide: approval for high-impact actions, the platform facts the tools do not check, Terraform ownership, targets, CLI use, and failure handling. Optional, since the core rules arrive with every connection. | Read | -| `get_cpln_skill` | Get a Control Plane Skill | The runbook for one task family: how to use a feature correctly, the constraints that are easy to miss, and when it's the wrong tool. Not advertised when the [AI Plugin](/ai/plugin) supplies its own skills. | Read | -| `plan_app` | Plan an App | Before any code for an app that keeps anything (content, records, files, or sign-ins): how Control Plane keeps its files, records, secrets, and replicas, and the tools that do each. The assistant settles what your request leaves open with you first. Not in `readonly`. | Read | -| `get_resource_schema` | Get Resource Schema & API Endpoints | The exact object schema and REST endpoints for a resource kind, for authoring an accurate manifest or calling the API. | Read | -| `get_permissions` | Get Permissions for a Resource Kind | The grantable permissions for a resource kind, for building policies. | Read | +| `get_cpln_skill` | Get a Control Plane Skill | The runbook for one task family: how to use a feature correctly, the constraints that are easy to miss, and when it's the wrong tool. The `operating-guide` skill covers what applies to every task. Not advertised when the [AI Plugin](/ai/plugin) supplies its own skills. | Read | +| `plan_app` | Plan an App | Before any code for an app that keeps anything (content, records, files, or sign-ins): how Control Plane keeps its files, records, secrets, and replicas, and the tools that do each. The assistant settles what your request leaves open with you first. | Read | +| `get_permissions` | Get Permissions for a Resource Kind | The grantable permissions for a resource kind, for building policies. | Read · `full` | | `search_control_plane` | Search Control Plane Docs | Search the documentation: up to five hits, each with a short excerpt and its page path. | Read | -| `query_docs_filesystem_control_plane` | Query Control Plane Docs Filesystem | Read a whole docs page, or search the docs pages and OpenAPI specs, with read-only, shell-like commands. | Read | +| `query_docs_filesystem_control_plane` | Query Control Plane Docs Filesystem | Read a whole docs page, or search the docs pages and OpenAPI specs, with read-only, shell-like commands. | Read · `full` | --- ## Generic resource tools -The single read and delete tools that work for every resource kind. Use these to list, inspect, or delete resources — pass the `kind` (`workload`, `secret`, `gvc`, and so on); secrets are read-only. All are in `core`. +The single read and delete tools that work for every resource kind. Use these to list, inspect, or delete resources: pass the `kind` (`workload`, `secret`, `gvc`, and so on). All are in `core`. | Name | Label | Description | Access | | :--- | :---- | :---------- | :----- | | `list_resources` | List Resources of a Kind | List resources of one kind as a summary table. | Read | -| `get_resource` | Get a Resource | Fetch one resource by kind and name, with its full JSON. Secrets return metadata only — the API never includes their data. | Read | -| `delete_resource` | Delete a Resource | Delete one resource by kind and name. The single delete tool for every deletable kind — secrets are not deletable. | Destructive | +| `get_resource` | Get a Resource | Fetch one resource by kind and name, with its full JSON. Secrets return metadata only: the API never includes their data. A volume set also lists its snapshots, the newest 10 per location and volume. With `schema: true`, it returns the kind's apply-ready schema and REST endpoints instead, for authoring an accurate manifest or calling the API. | Read | +| `delete_resource` | Delete a Resource | Delete one resource by kind and name. The single delete tool for every deletable kind, secrets included: deleting a secret breaks the workloads, GVC pull secrets, and domain TLS that use it. | Destructive | --- ## Jobs -Each job tool does a whole task in one call: it reads the state it needs, makes the change, waits up to about 40 seconds for the result, and returns the outcome. When the task needs longer, the result names the exact call that continues it. `create_secret` and `rotate_secret` under [Secrets](#secrets) and `create_domain` under [Custom domains](#custom-domains) work the same way. All are in `core`. +Each job tool does a whole task in one call: it reads the state it needs, makes the change, and returns the outcome. `deploy_app`, `add_database`, `restart_workload`, `rollback_workload`, `promote_workload`, and `grant_cloud_access` then wait up to 40 seconds by default for the result; when the task needs longer, the result names the exact call that continues it. All are in `core` except `promote_workload`, which is in `full`. | Name | Label | Description | Access | | :--- | :---- | :---------- | :----- | -| `deploy_app` | Deploy an App | Build and deploy an HTTP app: from the app files stored with `write_app_files`, a GitHub or GitLab repository, or an existing image. Creates or updates a standard workload with production defaults (health checks, and two replicas so a deploy never takes it offline), grants access to the secrets its env references, and returns the public URL once it is ready. `timeoutSeconds` raises the platform's 5 second request limit for slow requests, and more replicas than the org's quota allows are refused before anything is built. With no GVC named, it uses the GVC it created for apps, asks before using any other, and creates the org's first one in the location you choose. It replaces a workload it didn't create only after your explicit yes. Serverless, multi-container, cron, and non-HTTP workloads use `create_workload`. | Destructive | -| `add_database` | Add a Database | Install PostgreSQL, MySQL, MariaDB, MongoDB, or Redis from the Template Catalog with credentials Control Plane generates, let the named workloads connect, and return the host, port, and the env values an app uses as `cpln://secret` references. No credential passes through the chat. It starts one instance: Redis also has a failover tier, and PostgreSQL and MongoDB failover are separate templates. With no GVC named, it places the database the way `deploy_app` places an app. | Write | -| `diagnose_workload` | Diagnose a Workload | Find out why a workload is unhealthy: reads its deployments, events, and recent error logs, and returns each likely cause with its evidence and the fix. Also in `readonly`. | Read | +| `deploy_app` | Deploy an App | Build and deploy an HTTP app: from the app files stored with `write_app_files`, a GitHub or GitLab repository, or an existing image. Creates or updates a standard workload with production defaults (health checks, and two replicas so a deploy never takes it offline), or a stateful workload with one replica when the app gets block storage of its own instead of shared storage, grants access to the secrets its env references, and returns the public URL once it is ready. `timeoutSeconds` raises the platform's 5 second request limit for slow requests, and a referenced secret that does not exist, or more replicas than the org's quota allows, is refused before anything is built. With no GVC named, it uses the GVC it created for apps, asks before using any other, and creates the org's first one in the location you choose. It replaces a workload it didn't create only after your explicit yes. Serverless, multi-container, cron, and non-HTTP workloads use `create_workload`. | Destructive | +| `add_database` | Add a Database | Install PostgreSQL, MySQL, MariaDB, MongoDB, or Redis from the Template Catalog with credentials Control Plane generates, let the named workloads connect, and return the host, port, and the env values an app uses as `cpln://secret` references. No credential passes through the chat. It starts one instance: Redis also has a failover tier, and PostgreSQL and MongoDB failover are separate templates. With no GVC named, it places the database the way `deploy_app` places an app. | Destructive | +| `diagnose_workload` | Diagnose a Workload | Find out why a workload is unhealthy: reads its deployments, events, and recent error logs, and returns each likely cause with its evidence and the fix. | Read | | `restart_workload` | Restart a Workload | Replace every replica with a fresh one on the same spec, so the workload rereads its secrets and env, then wait until it is ready. | Destructive | | `rollback_workload` | Rollback a Workload | Put a workload back on its previous image, or on a given one, changing nothing else, then wait until it is ready. | Destructive | -| `promote_workload` | Promote a Workload | Run a workload in another GVC of the same org with its current spec, such as staging to production, with an optional image and env values for the target. Grants access to the secrets its env references and overwrites a workload of the same name there. | Destructive | -| `allow_workload_access` | Allow Workload Access | Let other workloads reach a workload through its internal firewall, keeping every other rule, and return the internal URL the callers use. Also takes callers back out. | Write | -| `grant_cloud_access` | Grant Cloud Access | Give a workload credential-free access to AWS, GCP, or Azure through an existing cloud account: sets up its identity, binds the access, and waits until the identity reports it usable. | Write | +| `promote_workload` | Promote a Workload | Run a workload in another GVC of the same org with its current spec, such as staging to production, with an optional image and env values for the target. Grants access to the secrets its env references, points its internal firewall links to workloads of its own GVC at the same names in the target, and creates or updates each volume set it mounts there with the source's settings, never copying volume data. A missing secret is refused before anything is written. Overwrites a workload of the same name there. | Destructive · `full` | +| `allow_workload_access` | Allow Workload Access | Let other workloads reach a workload through its internal firewall, keeping every other rule, and return the internal URL the callers use. Also takes callers off its list; a `same-gvc` or `same-org` rule still admits them. | Destructive | +| `grant_cloud_access` | Grant Cloud Access | Give a workload credential-free access to AWS, GCP, or Azure through an existing cloud account: sets up its identity, binds the access, and waits until the identity reports it usable. With an existing AWS role or GCP service account it returns at once, since the identity reports no status for one. It replaces the identity's existing binding for that provider, which can cut access for every workload that shares the identity. | Destructive | --- @@ -92,8 +86,8 @@ Create and manage [GVCs](/reference/gvc) and the cloud locations they span. | Name | Label | Description | Access | | :--- | :---- | :---------- | :----- | -| `create_gvc` | Create a Control Plane GVC | Create a GVC with one or more cloud locations. If you leave the location to the assistant, it picks one and tells you which. | Write | -| `update_gvc` | Update a Control Plane GVC | Update a GVC's settings, such as its description, tags, or pull secrets. | Destructive | +| `create_gvc` | Create a GVC | Create a GVC with one or more cloud locations. If you leave the location to the assistant, it picks one and tells you which. | Write | +| `update_gvc` | Update a GVC | Update a GVC's settings, such as its description, tags, or pull secrets. | Destructive | | `add_gvc_locations` | Add locations to a GVC | Add one or more cloud locations to an existing GVC. | Write · `full` | | `remove_gvc_locations` | Remove locations from a GVC | Remove cloud locations from a GVC. | Destructive · `full` | @@ -105,17 +99,14 @@ Create, update, run, and operate [workloads](/reference/workload/general). Deplo | Name | Label | Description | Access | | :--- | :---- | :---------- | :----- | -| `create_workload` | Create a Workload | Create a serverless, standard, stateful, or cron workload with its containers and exposure. | Write | -| `update_workload` | Update a Workload | Update an existing workload — image, resources, scaling, environment, and more. | Destructive | -| `grant_workload_secret_access` | Grant Workload Secret Access | Grant an existing workload access to a secret by wiring its identity and access policy. Never returns secret values. | Write | -| `workload_start_cron` | Workload — Start a Cron Run | Trigger an immediate run of a cron workload. | Destructive | -| `workload_stop_replica` | Workload — Stop a Replica | Terminate one running replica of a workload; the platform reschedules a replacement per the scaling settings. | Destructive | +| `create_workload` | Create a Workload | Create a serverless, standard, stateful, or cron workload with its containers, exposure, per-location overrides, and rollout and security options. | Write | +| `update_workload` | Update a Workload | Update an existing workload: image, resources, scaling, environment, per-location overrides (`localOptions`), rollout and security options, and more. Per-location overrides merge by location, and rollout and security options merge field by field, so settings the call leaves out keep their values. `public: true` adds `0.0.0.0/0` to its external inbound and outbound allow lists, and `public: false` removes `0.0.0.0/0` and `::/0` from its external inbound; every other firewall rule stays. | Destructive | +| `grant_workload_secret_access` | Grant Workload Secret Access | Grant an existing workload access to a secret by wiring its identity and access policy. With `remove: true`, it revokes that access and keeps the identity. Never returns secret values. | Destructive | +| `start_workload_cron` | Start a Workload Cron Run | Trigger an immediate run of a cron workload. Returns a command ID that `get_command` follows. | Destructive | +| `stop_workload_replica` | Stop a Workload Replica | Terminate one running replica of a workload; the platform reschedules a replacement per the scaling settings. | Destructive · `full` | | `configure_workload_load_balancer` | Configure Workload Load Balancer | Set or clear the direct per-location load balancer, geo headers, and replica-direct routing. | Destructive · `full` | -| `configure_workload_security` | Configure Workload Security Options | Set or clear the pod-level security context (filesystem group, run-as user). | Destructive · `full` | -| `configure_workload_rollout` | Configure Workload Rollout Options | Set or clear how updates roll out — surge, unavailability, and scaling policy. | Destructive · `full` | | `configure_workload_retry` | Configure Workload Request Retry Policy | Set or clear the retry behavior for failed requests. | Destructive · `full` | | `configure_workload_sidecar` | Configure Workload Envoy Sidecar | Set or clear the Envoy proxy filter chain. Advanced. | Destructive · `full` | -| `configure_workload_local_options` | Configure Workload Per-Location Options | Set or clear per-location overrides of the default options. | Destructive · `full` | | `configure_workload_extras` | Configure Workload BYOK Extras | Set or clear BYOK-only Kubernetes customizations (affinity, tolerations, spread constraints). | Destructive · `full` | --- @@ -126,63 +117,54 @@ Build container [images](/reference/image) on Control Plane and follow the build | Name | Label | Description | Access | | :--- | :---- | :---------- | :----- | -| `write_app_files` | Write App Files | Write or edit an app's files (whole text files, appends for a large file in parts, exact-text edits, deletions, and moves or renames, uploaded files included) under an app name, for chats that cannot write files or agents without the `cpln` CLI (an agent with a filesystem and the CLI writes a local folder and builds it with the CLI instead). Files you have to provide yourself, such as pictures, come in through an upload link from `create_app_files_upload_link`; files attached to the chat cannot be forwarded. The files are stored on Control Plane between calls; the same name is the image name `build_image` produces. Limits match GitHub's: 100 MB per file, 1 GB per app. | Write | +| `write_app_files` | Write App Files | Write or edit an app's files (whole text files, appends for a large file in parts, exact-text edits, deletions, and moves or renames, uploaded files included) under an app name. Files you have to provide yourself, such as pictures, come in through an upload link from `create_app_files_upload_link`; files attached to the chat cannot be forwarded. The files are stored on Control Plane between calls; the same name is the image name `build_image` produces. Limits: 100 MB per file, 1 GB per app. | Destructive | | `build_image` | Build an Image | Build an image from a GitHub or GitLab repository, or from the app files stored with `write_app_files`, and push it to the org's private registry. Returns a build id; the build keeps running after the call returns. Building an existing `NAME:TAG` replaces that image. | Destructive | | `get_image_build` | Get an Image Build Status and Log | Read a build's status, progress events, and log by its build id. | Read | -| `create_app_files_upload_link` | Create an App Files Upload Link | Get an upload link for files you have to provide (pictures, fonts, PDFs, data). Open it in a browser and drop every file at once: the page sorts each one into place, such as the logo into its own spot and a set of photos into their folder, where order does not matter, and you can move any file before uploading. The link works for an hour, up to 100 MB per file. | Write | -| `get_app_files` | Get App Files | List an app's stored files, read one (in slices for a large file), or get a short-lived download link (tar.gz) for all of them. Also reports where each of the app's images was built from: stored files, a repository, a folder uploaded with the CLI, or a push outside a Control Plane build. | Read | +| `create_app_files_upload_link` | Create an App Files Upload Link | Get an upload link for files you have to provide (pictures, fonts, PDFs, data). Open it in a browser and drop the files in. The link works for an hour, up to 100 MB per file. | Write | +| `get_app_files` | Get App Files | List an app's stored files, read one (in slices for a large file), or get a short-lived download link (tar.gz) for all of them. Also reports where each of the app's images was built from: stored files, a repository, a folder uploaded with the CLI, or a push outside a Control Plane build. A file holding credentials is never shown. | Read | -No Docker daemon is involved: the build service clones the repository or assembles the stored app files, detects how to build it (using the Dockerfile when one is present, otherwise by auto-detecting the stack), and always produces a `linux/amd64` image. Building an existing `NAME:TAG` replaces that image, so prefer a fresh tag. `deploy_app` under [Jobs](#jobs) builds and deploys in one call. +No Docker daemon is involved: the build service clones the repository or assembles the stored app files, detects how to build it (using the Dockerfile when one is present, otherwise by auto-detecting the stack), and always produces a `linux/amd64` image. `deploy_app` under [Jobs](#jobs) builds and deploys in one call. The MCP server has no access to your filesystem, so a folder on your machine builds through the CLI instead: [`cpln image build --remote --dir PATH --name NAME:TAG`](/cli-reference/get-started/images#build-options) uploads the folder and builds it the same way, also without Docker. -A **private** repository needs a one-time browser authorization per organization. The first build that needs it returns an authorization link instead of starting a build; open the link, approve access, then ask the assistant to build again. The link is single-use and expires shortly. Later builds in that organization run without prompting. +A **private** repository needs a one-time browser authorization per organization. The first build that needs it returns an authorization link instead of starting a build; open the link, approve access, then ask the assistant to build again. The link is single-use and expires in about 15 minutes. Later builds in that organization run without prompting. --- ## Secrets -[Secret](/reference/secret) values never pass through the chat. `list_resources` and `get_resource` show a secret's metadata, never its data. `create_secret` fills a new secret with values Control Plane generates, or hands you a Console link where you type values only you have; `rotate_secret` replaces the values and restarts the workloads that use them. No tool accepts, reveals, or deletes a secret value, and deleting a secret stays in the [Console](https://console.cpln.io), the [CLI](/cli-reference/overview), [Terraform](/iac/terraform), [Pulumi](/iac/pulumi), or the [API](/api-reference/api). To let a workload read a secret, use `grant_workload_secret_access` under [Workloads](#workloads); `deploy_app` and `promote_workload` grant it for the secrets their env references. +[Secret](/reference/secret) values never pass through the chat. `list_resources` and `get_resource` show a secret's metadata, never its data. `create_secret` fills a new secret with values Control Plane generates, or hands you a Console link where you type values only you have; `rotate_secret` replaces the values and restarts the workloads that use them. No tool accepts or reveals a secret value. `delete_resource` under [Generic resource tools](#generic-resource-tools) deletes a secret. To let a workload read a secret, use `grant_workload_secret_access` under [Workloads](#workloads); `deploy_app` and `promote_workload` grant it for the secrets their env references. | Name | Label | Description | Access | | :--- | :---- | :---------- | :----- | -| `create_secret` | Create a Secret | Create a secret with random values Control Plane generates now, or get a Console link prefilled with its name, type, and keys where you type the values. No input accepts a value and no result contains one. | Write | -| `rotate_secret` | Rotate a Secret | Replace a secret's values, generated again or entered by you through a Console link, and restart the workloads that use it. Anything outside Control Plane that holds the old values stops working. | Destructive | +| `create_secret` | Create a Secret | Create a secret with random values Control Plane generates now, or get a Console link prefilled with its name, type, and keys where you type the values. For a dictionary, the link can also fill in starting values for keys that are not secret, such as a host or port, which you can change before saving. No input accepts a value and no result contains one. | Write | +| `rotate_secret` | Rotate a Secret | Replace a secret's values, generated again or entered by you through a Console link, and restart the workloads that use it, waiting up to 40 seconds by default for them to be ready. Anything outside Control Plane that holds the old values stops working. | Destructive | --- ## Custom domains -Configure [custom domains](/reference/domain), their port listeners, routes, TLS, and CORS. Port, route, TLS, and CORS tools operate on an existing domain. +Configure [custom domains](/reference/domain), their port listeners, routes, TLS, and CORS. `update_domain` changes all of them on an existing domain. | Name | Label | Description | Access | | :--- | :---- | :---------- | :----- | -| `create_domain` | Create a Domain | Put a domain in front of a workload: from the domain, workload, and GVC it derives the 443 listener, the route, and `cname` mode, and returns the DNS records to add. A custom setup (other listeners, `ns` mode, several routes) passes the ports and DNS mode instead. | Write | -| `update_domain` | Update a Domain | Update a domain's metadata, host/subdomain acceptance flags, and GVC or workload binding. | Destructive | -| `add_domain_port` | Add a Domain Port Listener | Add a port listener (for example, 443/http2) to a domain. | Write | -| `remove_domain_port` | Remove a Domain Port Listener | Remove a port listener from a domain. | Destructive | -| `add_domain_route` | Add a Route to a Domain Listener | Add a route mapping a path or prefix to a workload. | Write | -| `update_domain_route` | Update a Route on a Domain Listener | Update an existing route on a domain listener. | Destructive | -| `remove_domain_route` | Remove a Route from a Domain Listener | Remove a route from a domain listener. | Destructive | -| `set_domain_tls` | Set TLS on a Domain Listener | Set the TLS configuration on a domain listener. | Destructive | -| `clear_domain_tls` | Clear TLS on a Domain Listener | Clear the TLS configuration from a domain listener. | Destructive | -| `set_domain_cors` | Set CORS on a Domain Listener | Set the CORS policy on a domain listener. | Destructive · `full` | -| `clear_domain_cors` | Clear CORS on a Domain Listener | Clear the CORS policy from a domain listener. | Destructive · `full` | +| `create_domain` | Create a Domain | Put a domain in front of a workload: from the domain, workload, and GVC it derives the 443 listener, the route, and `cname` mode, and returns the DNS records to add. A custom setup (other listeners, `ns` mode, several routes) passes the ports and DNS mode instead. A route can split traffic across workloads by weight (canaries) or mirror it. | Write | +| `update_domain` | Update a Domain | Update a domain's metadata, host/subdomain acceptance flags, certificate challenge (not for `.internal` domains), GVC or workload binding, and listeners and routes. It adds a listener or changes its protocol, TLS, or CORS, adds a route or replaces the one with the same match, and removes listeners, routes, TLS, or CORS. Routes take the same canaries and mirroring as in `create_domain`. | Destructive | --- ## Identity & access -Manage [identities](/reference/identity), [policies](/reference/policy), [groups](/reference/group), [service accounts](/reference/serviceaccount), and the private-network resources attached to an identity. Groups, service accounts, network resources, and user invitations are in `full`. Credential-free cloud access for a workload takes one call with `grant_cloud_access` under [Jobs](#jobs). +Manage [identities](/reference/identity), [policies](/reference/policy), [groups](/reference/group), [service accounts](/reference/serviceaccount), and the private-network resources attached to an identity. All are in `full`. In `core`, credential-free cloud access for a workload takes one call with `grant_cloud_access` under [Jobs](#jobs), and secret access with `grant_workload_secret_access` under [Workloads](#workloads). | Name | Label | Description | Access | | :--- | :---- | :---------- | :----- | -| `create_identity` | Create an Identity | Create a GVC-scoped identity for workload access to secrets, cloud resources, and private networks. | Write | -| `update_identity` | Update an Identity | Update an identity's settings and bound credentials. | Destructive | -| `create_policy` | Create a Policy | Create a policy that grants permissions on resources to principals. | Write | -| `update_policy` | Update a Policy | Update a policy's bindings, targets, or permissions. | Destructive | +| `create_identity` | Create an Identity | Create a GVC-scoped identity for workload access to secrets, cloud resources, and private networks. | Write · `full` | +| `update_identity` | Update an Identity | Update an identity's settings and bound credentials. | Destructive · `full` | +| `create_policy` | Create a Policy | Create a policy that grants permissions on resources to principals. | Write · `full` | +| `update_policy` | Update a Policy | Update a policy's bindings, targets, or permissions. | Destructive · `full` | | `create_group` | Create a Group | Create a group of users and service accounts, with optional tag-based membership. | Write · `full` | | `edit_group` | Edit a Group | Update a group's membership or membership query. | Destructive · `full` | | `create_service_account` | Create a Service Account | Create a service account for programmatic access. | Write · `full` | @@ -198,71 +180,60 @@ Manage [identities](/reference/identity), [policies](/reference/policy), [groups ## Template catalog -Browse and operate the [Template Catalog](/template-catalog/overview): production-ready stacks you install instead of hand-authoring resources. For PostgreSQL, MySQL, MariaDB, MongoDB, or Redis, `add_database` under [Jobs](#jobs) installs the template with generated credentials in one call. +Browse and operate the [Template Catalog](/template-catalog/overview): production-ready stacks you install instead of hand-authoring resources. For PostgreSQL, MySQL, MariaDB, MongoDB, or Redis, `add_database` under [Jobs](#jobs) installs the template with generated credentials in one call; `install_template` takes them only for a setting `add_database` does not take, such as backups. | Name | Label | Description | Access | | :--- | :---- | :---------- | :----- | -| `browse_templates` | Browse Template Catalog | List the catalog — name, category, latest version, and whether each template creates its own GVC. | Read | -| `get_template` | Get Template Detail & Example Values | Show a template's versions, prerequisites, and example `values.yaml`. | Read | -| `install_template` | Install Template | Install a catalog template as a new release. With `dryRun: true`, it renders what the install would create and applies nothing. | Write | -| `upgrade_template` | Upgrade Template | Upgrade an installed release to a new version or updated values. | Destructive | +| `browse_templates` | Browse Template Catalog | Find templates for the capability or product needed, named in plain words (`query`), with their prerequisites, and the template to pick instead for other needs. Without `query`, list the catalog and its categories, or one `category`. With `name`, show that template's versions, what it deploys, its prerequisites, the README's important notes, and example `values.yaml`; `readmeSection` returns one README section. | Read | +| `install_template` | Install Template | Install a catalog template as a new release. With `dryRun: true`, it renders what the install would create and applies nothing. It refuses, creating nothing, while the release would read a required secret that does not exist. | Write | +| `upgrade_template` | Upgrade Template | Upgrade an installed release to a new version with its complete values, which replace the current ones. It refuses, changing nothing, while the release would read a required secret that does not exist. | Destructive | | `rollback_template` | Rollback a Template Release | Roll a release back to a prior revision. | Destructive · `full` | -| `uninstall_template` | Uninstall Template | Uninstall a release and remove the resources it created. | Destructive | -| `list_installed_templates` | List Installed Templates | List the template releases installed in an org. | Read | -| `get_installed_template` | Get Installed Template Resources | Show a release's status, revision, and the resources it created. | Read | +| `uninstall_template` | Uninstall Template | Uninstall a release and remove the resources it created, including volume data. | Destructive | +| `get_installed_template` | Get Installed Templates | Show a release's status, revision, and the resources it created. Without a release name, list the releases installed in the org, up to 50. | Read | --- ## Volume sets & storage -Create [volume sets](/reference/volumeset), mount them to workloads, and manage their volumes and snapshots. Snapshot and per-volume operations are in `full`. +Create [volume sets](/reference/volumeset), mount them to workloads, and manage their volumes and snapshots. Per-volume operations and snapshot changes go through `run_volumeset_command`, and `get_resource` lists a volume set's snapshots. Creating a volume set on its own is in `full`; `mount_volumeset_to_workload` creates a missing one. | Name | Label | Description | Access | | :--- | :---- | :---------- | :----- | -| `create_volumeset` | Create a Volumeset | Create a volume set for persistent storage. | Write | +| `create_volumeset` | Create a Volumeset | Create a volume set for persistent storage. | Write · `full` | | `update_volumeset` | Update a Volumeset | Update a volume set's settings. A new snapshot policy without a retention keeps the current one, so scheduled snapshots always expire. | Destructive | | `mount_volumeset_to_workload` | Mount Volumeset to Workload | Mount a volume set to a workload at a path (ext4/xfs require a stateful or VM workload; shared mounts on any type). | Write | -| `expand_volumeset` | Expand Volumeset Volume | Increase a volume's capacity with no downtime or data loss. | Write | -| `shrink_volumeset` | Shrink Volumeset Volume | Shrink a volume to a smaller capacity. Data is migrated, but if used bytes exceed the new size the excess is lost — snapshot first. | Destructive · `full` | -| `create_volumeset_snapshot` | Create Volumeset Snapshot | Create a point-in-time snapshot of a volume. | Write · `full` | -| `list_volumeset_snapshots` | List Volumeset Snapshots | List the snapshots of a volume set. | Read · `full` | -| `restore_volumeset_snapshot` | Restore Volumeset Snapshot | Restore a snapshot, discarding everything written since it was taken. | Destructive · `full` | -| `delete_volumeset_snapshot` | Delete Volumeset Snapshot | Delete a volume set snapshot. | Destructive · `full` | -| `delete_volumeset_volume` | Delete Volumeset Volume | Delete a single volume from a volume set. | Destructive · `full` | +| `run_volumeset_command` | Run a Volume Set Command | Run one command on one volume of a volume set, picked by location and index: expand it live (at most 4 times per volume in a rolling 24 hours), shrink it (its data moves to a smaller volume, no smaller than the initial capacity or the used bytes plus 5%, and the replica restarts), create, restore, or delete a snapshot, delete the volume, or copy it to the same index in another location or volume set. Restoring discards everything written since the snapshot, deleting a volume loses its data for good, and a copy replaces the volume at the destination. Shrinking, creating or restoring a snapshot, deleting a volume, and copying it need ext4 or xfs. Returns a command ID that `get_command` follows. | Destructive | --- ## Observability -Inspect deployments, logs, events, replicas, metrics, traces, the audit trail, and the status of asynchronous operation commands. All are in `core`. +Inspect deployments, logs, events, replicas, metrics, traces, the audit trail, and the status of asynchronous operation commands. All are in `core` except the event, replica, and command lists, which are in `full`. | Name | Label | Description | Access | | :--- | :---- | :---------- | :----- | -| `list_deployments` | List Workload Deployments | A workload's per-location rollout status — the primary readiness check after a deploy. | Read | -| `get_workload_logs` | Get Workload Logs | Query a workload's logs by structured parameters or a raw LogQL query. | Read | -| `get_workload_events` | Get Workload Events | Fetch a workload's event log to diagnose readiness, liveness, and deploy failures. | Read | -| `list_workload_replicas` | List Workload Replicas | List the running replicas of a workload in a location. | Read | -| `list_metrics` | List & Discover Metrics | Discover the metrics and labels you can query, each with a correct PromQL template. | Read | -| `query_metrics` | Query Workload Metrics (PromQL) | Run a PromQL query against Control Plane metrics. | Read | +| `list_deployments` | List Workload Deployments | A workload's per-location rollout status, the primary readiness check after a deploy. Each location also names its running replicas. | Read | +| `get_workload_logs` | Get Workload Logs | Query the logs of a workload, or of an mk8s cluster with the logs add-on, by structured parameters or a raw LogQL query. | Read | +| `get_workload_events` | Get Workload Events | Fetch a workload's event log to diagnose readiness, liveness, and deploy failures. | Read · `full` | +| `list_workload_replicas` | List Workload Replicas | List the running replicas of a workload in a location. | Read · `full` | +| `query_metrics` | Query Workload Metrics (PromQL) | Run a PromQL query against Control Plane metrics. Without a query, it lists the metrics you can query instead: the documented ones, each with a correct PromQL template, and every metric present in the org, including those mk8s clusters ship, or one metric's live label values. | Read | | `query_audit_events` | Query Audit Events | Query the audit trail for mutations on resources of a kind. | Read | -| `query_traces` | Query Distributed Traces | Search distributed traces, newest first, by GVC, workload, HTTP method, request id, errors, or duration, or with TraceQL. | Read | -| `get_trace` | Get Trace | Fetch one trace by ID and summarize its span tree with each span's duration, method, path, and status, its request ids, and its errors. | Read | -| `list_commands` | List Commands | List the asynchronous operation commands issued against a workload or volume set — cron runs, replica stops, and volume operations. | Read | +| `query_traces` | Query Distributed Traces | Search distributed traces, newest first, by GVC, workload, HTTP method, request id, errors, or duration, or with TraceQL. With a trace ID, it fetches that trace instead and summarizes its span tree with each span's duration, method, path, and status, its request ids, and its errors. | Read | +| `list_commands` | List Commands | List the asynchronous operation commands issued against a workload or volume set: cron runs, replica stops, and volume operations. | Read · `full` | | `get_command` | Get a Command | Fetch one asynchronous command by ID to check a long-running operation's status. | Read | --- ## Terraform & IaC -Convert manifests to Terraform and export existing resources as HCL for [infrastructure as code](/iac/overview). `export_terraform` exports one resource, or every resource under a path such as `/org/ORG` or `/org/ORG/gvc/GVC/workload`. +Convert manifests to Terraform and export existing resources as HCL for [infrastructure as code](/iac/overview). | Name | Label | Description | Access | | :--- | :---- | :---------- | :----- | -| `convert_to_terraform` | Convert a manifest to Terraform | Convert a resource manifest to Terraform HCL, dry-run validated against the API first. | Read | -| `export_terraform` | Export an existing resource to Terraform | Generate Terraform HCL for existing resources from a self link, single or in bulk. | Read | +| `export_terraform` | Export to Terraform | Generate Terraform HCL for one existing resource by self link, or for every resource under a path such as `/org/ORG` or `/org/ORG/gvc/GVC/workload`, or convert a resource manifest, dry-run validated against the API first. Secrets are never exported or converted: an export that would include a secret is refused, so narrow the path to leave secrets out. | Read | - Terraform and Pulumi tag every resource they manage with `cpln/managedByTerraform: "true"`. When `get_resource` reads such a resource, or any tool changes one, the result tells the assistant to warn you: a change made through the MCP server drifts from your code, and the next apply can revert it unless your code gets the change too. + Terraform and Pulumi tag every resource they manage with `cpln/managedByTerraform: "true"`. When `get_resource` reads such a resource, or a tool updates one, the result tells the assistant to warn you: a change made through the MCP server drifts from your code, and the next apply can revert it unless your code gets the change too. --- @@ -278,7 +249,7 @@ Onboard [cloud accounts](/reference/cloudaccount), create [agents](/reference/ag | `update_cloud_account` | Update a Cloud Account | Update a cloud account's settings. | Destructive · `full` | | `create_agent` | Create an Agent | Start creating an agent for secure connectivity to a private network: returns a Console link with its name and description prefilled, where you finish and download the bootstrap config. The config never passes through the chat. | Read · `full` | | `update_agent` | Update an Agent | Update an agent's settings. | Destructive · `full` | -| `get_agent_info` | Get Agent Info | Fetch an agent's real-time status — active state, last heartbeat, peer and service counts. | Read · `full` | +| `get_agent_info` | Get Agent Info | Fetch an agent's real-time status: active state, last heartbeat, peer and service counts. | Read · `full` | | `get_agent_eventlog` | Get Agent Event Log | Fetch an agent's event log. | Read · `full` | | `create_ipset` | Create an IP Set | Reserve static public IPs per location. | Write · `full` | | `update_ipset` | Update an IP Set | Update an IP set's settings. | Destructive · `full` | @@ -289,14 +260,15 @@ Onboard [cloud accounts](/reference/cloudaccount), create [agents](/reference/ag ## Organization & operations -Find the [orgs](/reference/org) a connection can use, and manage org settings, [quotas](/reference/quota), [audit contexts](/reference/auditctx), and [external logging](/external-logging/overview). All are in `full` except `list_orgs` and `list_quotas`. +Find the [orgs](/reference/org) a connection can use, and manage org settings, [quotas](/reference/quota), [audit contexts](/reference/auditctx), and [external logging](/external-logging/overview). All are in `full` except `list_orgs` and `request_quota_increase`. | Name | Label | Description | Access | | :--- | :---- | :---------- | :----- | -| `list_orgs` | List Organizations | The orgs this connection can use (the ones you selected when you connected), each with its GVCs and their locations. With a single org selected, every tool uses it when no org is named. Also in `readonly`. | Read | +| `list_orgs` | List Organizations | The orgs this connection can use (the ones you selected when you connected), each with its GVCs and their locations. With a single org selected, every tool uses it when no org is named. | Read | | `update_org` | Update Organization Settings | Update organization-wide settings. | Destructive · `full` | -| `list_quotas` | List Organization Quotas | List the org's resource quotas with current usage — filter to those near their limit. | Read | +| `list_quotas` | List Organization Quotas | List the org's resource quotas with current usage (optionally only those near their limit). | Read · `full` | | `get_quota` | Get Quota Details | Fetch the details and current usage of one quota. | Read · `full` | +| `request_quota_increase` | Request a Quota Increase | Ask Control Plane to raise an org quota after an action fails on it. A per-GVC quota takes the GVC. Every call files a new request, which comes back approved (the new max applies now), pending (Control Plane reviews it), or denied with the reason. Lowering a max goes through Control Plane support. | Write | | `create_audit_context` | Create Audit Context | Create a custom audit context for tamper-proof external events. | Write · `full` | | `edit_audit_context` | Edit an Audit Context | Update an audit context's description and tags. Replaces the description and can clear existing tags. | Destructive · `full` | | `get_external_logging` | Get External Logging Configuration | View the org's primary and extra external logging providers. | Read · `full` | @@ -311,7 +283,7 @@ Provision and update [managed Kubernetes](/mk8s/overview) clusters on every supp | Name | Label | Description | Access | | :--- | :---- | :---------- | :----- | -| `create_mk8s` | Create an mk8s Cluster | Create a cluster on AWS, GCP, Azure, DigitalOcean, Hetzner, Linode, Lambda Labs, Oblivus, Paperspace, or Triton, or on hardware you own with `generic`. The provider's credential secret must exist first; the tool names any required setting that is missing. | Write · `mk8s` | +| `create_mk8s` | Create an mk8s Cluster | Create a cluster on AWS, GCP, Azure, DigitalOcean, Hetzner, Linode, Lambda Labs, Oblivus, Paperspace, or Triton, or on hardware you own with `generic`. The tool names any required setting that is missing. | Write · `mk8s` | | `update_mk8s` | Update an mk8s Cluster | Update a cluster by sending only what changes. Node pool changes provision or remove nodes, region and network settings cannot change, and the Kubernetes version moves one minor version at a time. | Destructive · `mk8s` | --- diff --git a/llms.txt b/llms.txt index f3576d14..2a8c259e 100644 --- a/llms.txt +++ b/llms.txt @@ -20,7 +20,7 @@ Key entry points: - Console UI: https://console.cpln.io - CLI tool: `cpln` (install via npm `@controlplane/cli`, Homebrew `brew tap controlplane-com/cpln && brew install cpln`, or binary download) - REST API: https://api.cpln.io -- MCP Server: https://mcp.cpln.io/mcp (over 100 tools in four toolset profiles: readonly, core, mk8s, full) +- MCP Server: https://mcp.cpln.io/mcp (95 tools in four toolset profiles: readonly, core, mk8s, full) - AI Plugin: https://docs.controlplane.com/ai/plugin (skills, specialized agents, slash commands, and guardrail rules for AI coding assistants) - Terraform Provider: registry.terraform.io/providers/controlplane-com/cpln - OpenAPI Specs: https://api.cpln.io/openapi.json (Core), https://audit.cpln.io/openapi.json (Audit) @@ -235,8 +235,8 @@ Volume sets provide persistent storage within a GVC. Mount via `cpln://volumeset - **Both `fileSystemType` AND `performanceClass` are immutable** after creation. Changing either = delete + recreate (data loss). - Custom encryption (AWS KMS) is supported on `ext4`/`xfs` only. -- Volumes can only be **expanded once every 6 hours**. -- `shrinkVolume` provisions a NEW smaller volume and **permanently deletes the old volume and all its data** — only safe for distributed systems with replication (Kafka, CockroachDB). Never use for single-replica stateful workloads or databases without replication. +- A volume can be **expanded at most 4 times in a rolling 24 hours**. +- `shrinkVolume` **keeps the data**: it copies it to a NEW smaller volume, swaps that in (restarting the replica that uses it), and keeps the old volume if the copy fails. It is refused below the volume set's `initialCapacity`, below the used bytes plus 5%, or when the volume has no current usage metric. - `copyVolume` copies one volume into another location of the same volume set, or into another volume set in the same org (`spec.targetVolumeSet`). It **deletes the destination volume's storage device**, always requires `acknowledgeDestinationVolumeDeletion: true`, needs `exec.copyVolume` (not included in `exec`), and supports `ext4`/`xfs` only. A copy from a mounted source is not crash-consistent. - `createFinalSnapshot: true` (default) auto-snapshots before any volume deletion — leave on. - Volume sets are GVC-scoped. A workload can only use volume sets in the same GVC. Autoscaling (reactive and predictive) is available for automatic expansion. @@ -280,7 +280,7 @@ Pull secrets are configured at the **GVC level** (`spec.pullSecretLinks`), not p - For Kubernetes users: **mk8s** for new managed clusters, **BYOK** for existing infrastructure, **Kubernetes Operator** for managing Control Plane resources from Kubernetes manifests (ArgoCD/GitOps). - The **Template Catalog** provides 30+ production-ready templates (Postgres, Redis, Kafka, MongoDB, NATS, Elasticsearch, Nginx, etc.) — recommend these before writing custom manifests for common infrastructure. - For Infrastructure as Code: **Terraform** (registry.terraform.io/providers/controlplane-com/cpln) or **Pulumi**. -- The **MCP Server** at https://mcp.cpln.io/mcp (over 100 tools) is the recommended way for AI agents to interact with Control Plane programmatically. Its job tools do a whole task in one call, such as deploying an app, adding a database, creating or rotating a secret without its value entering the chat, or diagnosing a workload. +- The **MCP Server** at https://mcp.cpln.io/mcp (95 tools) is the recommended way for AI agents to interact with Control Plane programmatically. Its job tools do a whole task in one call, such as deploying an app, adding a database, creating or rotating a secret without its value entering the chat, or diagnosing a workload. - The **AI Plugin** at /ai/plugin bundles specialized agents, slash commands, and guardrail rules for AI coding assistants — install it for end-to-end execution of multi-step Control Plane workflows. ### JWT Authentication diff --git a/public/cli-conventions.md b/public/cli-conventions.md index 341e3858..fa40f2ba 100644 --- a/public/cli-conventions.md +++ b/public/cli-conventions.md @@ -5,7 +5,7 @@ alwaysApply: true # cpln CLI Conventions -**Never write a cpln command from memory.** Verify with `cpln --help` or `mcp__cpln__cpln_suggest`. +**Never write a cpln command from memory.** Verify with `cpln --help`. ## Command Structure diff --git a/reference/volumeset.mdx b/reference/volumeset.mdx index d7af97f3..d31e6a3c 100644 --- a/reference/volumeset.mdx +++ b/reference/volumeset.mdx @@ -34,7 +34,7 @@ The shared filesystem does not support: - When a volume is created, it will have an initial capacity defined by the spec of your volume set. - Volume capacity can be increased by sending an `expandVolume` command. -- Volume capacity can be decreased using the `shrinkVolume` command, but this causes data loss (see [Volume Shrinkage](#volume-shrinkage) below). +- Volume capacity can be decreased using the `shrinkVolume` command, which copies the data to a smaller volume (see [Volume Shrinkage](#volume-shrinkage) below). - The bill for a volume set is calculated by summing the reserved GB of all volumes. ## Autoscaling @@ -440,7 +440,7 @@ Volume sets support imperative operations on individual volumes and snapshots. E ## Volume Expansion -Volumes can be expanded on-demand by issuing an `expandVolume` command. If the volume set is in-use by a workload, the corresponding workload replica will be restarted. +Volumes can be expanded on-demand by issuing an `expandVolume` command. A volume in use by a workload keeps serving: `ext4` and `xfs` volumes are resized online, and `shared` volumes need no restart. If an online resize times out, the command either restarts the replica to finish the resize or reverts it, depending on the storage driver. You can only expand a volume 4 times in a rolling 24-hour period. Please plan accordingly. @@ -471,23 +471,9 @@ For example: Volumes can be shrunk on-demand by issuing a `shrinkVolume` command. If the volume set is in-use by a workload, the corresponding workload replica will be restarted. - - -**CRITICAL: Shrinking a volume causes PERMANENT DATA LOSS.** - -The `shrinkVolume` command provisions a new volume at the smaller size. Existing data is not migrated - the old volume and all its data are permanently deleted. - -**Only use `shrinkVolume` for applications with built-in data redundancy:** -- Apache Kafka (with proper replication factor) -- Distributed databases with replication (Cassandra, CockroachDB, etc.) -- Applications where data can be rebuilt from other replicas +The `shrinkVolume` command provisions a new volume at the smaller size, copies the existing data to it, and swaps it in. If the copy fails, the volume keeps its original storage device and data. -**Do not use `shrinkVolume` for:** -- Single-replica stateful workloads -- Databases without replication -- Any application where data loss is unacceptable - - +A volume cannot be shrunk below the volume set's `initialCapacity` or below its used bytes plus 5%. The command is also refused when no current usage metric is available for the volume. The `shrinkVolume` command is only available for `ext4` and `xfs` filesystems. It is not supported for `shared` filesystems. @@ -507,7 +493,63 @@ For example: "spec": { "location": "aws-sa-east-1", "volumeIndex": 0, - "newStorageCapacity": 5 + "newStorageCapacity": 10 + } +} +``` + +## Volume Copy + +A `copyVolume` command copies one volume into another location of the same volume set, or into a volume of another volume set in the same org, including one in another [GVC](/reference/gvc). The copy lands on the same volume index at the destination and replaces the volume there. The source volume is never modified. + + + +**The destination volume is replaced, and its storage device is DELETED.** + +Whatever storage device occupies the volume at `volumeIndex` in the destination is deleted once the copy is committed, and its data is lost. Every `copyVolume` command must set `acknowledgeDestinationVolumeDeletion` to `true`, even when the volume set status shows no device at the destination: status can lag behind reality. + + + +`copyVolume` is only available for `ext4` and `xfs` volume sets, on both ends. It is not supported for `shared` file systems. + +### copyVolume + +Spec: + +- `location`: the location the volume is copied **from**. Required. +- `volumeIndex`: the index of the volume to copy. The copy replaces the volume at the same index at the destination. Required. +- `targetLocation`: the location the volume is copied **to**. Required. +- `acknowledgeDestinationVolumeDeletion`: must be `true`. Confirms that the destination volume is replaced and its storage device deleted. +- `targetVolumeSet`: optional. A link to the volume set that receives the copy, in the same org. It defaults to the volume set the command is issued on. It accepts these forms: + - `//volumeset/VOLUME_SET`: a volume set in the same GVC + - `//gvc/GVC/volumeset/VOLUME_SET` + - `/org/ORG/gvc/GVC/volumeset/VOLUME_SET` + +To copy a volume into another location of the same volume set: + +```json copyVolume Command +{ + "type": "copyVolume", + "spec": { + "location": "aws-us-east-2", + "volumeIndex": 0, + "targetLocation": "aws-us-west-2", + "acknowledgeDestinationVolumeDeletion": true + } +} +``` + +To copy a volume into a volume set in another GVC: + +```json copyVolume Command (another volume set) +{ + "type": "copyVolume", + "spec": { + "location": "aws-us-east-2", + "volumeIndex": 0, + "targetLocation": "aws-us-west-2", + "targetVolumeSet": "//gvc/my-other-gvc/volumeset/my-volume-set", + "acknowledgeDestinationVolumeDeletion": true } } ``` @@ -826,7 +868,7 @@ The permissions below are used to define [policies](/reference/policy) together | exec.deleteVolumeSnapshot | Delete a volume snapshot | | | exec.expandVolume | Increase the storage capacity of a volume | | | exec.restoreVolume | Restore a volume to a snapshot | | -| exec.shrinkVolume | Shrink a volume (causes data loss) | | +| exec.shrinkVolume | Decrease the storage capacity of a volume | | | manage | Full access | create, delete, edit, exec, exec.copyVolume, exec.createVolumeSnapshot, exec.deleteOrphanedVolume, exec.deleteVolume, exec.deleteVolumeSnapshot, exec.expandVolume, exec.restoreVolume, exec.shrinkVolume, view | | view | Read-only access | | diff --git a/skill.md b/skill.md index 12b4c0e3..53337477 100644 --- a/skill.md +++ b/skill.md @@ -22,7 +22,7 @@ Control Plane is a hybrid platform for deploying and managing containerized work - Console: https://console.cpln.io - API: https://api.cpln.io - AI Plugin: `controlplane-com/ai-plugin` (recommended for Claude Code, Codex, Antigravity CLI — bundles MCP Server with skills, agents, slash commands, and guardrails) -- MCP Server: `https://mcp.cpln.io/mcp` (over 100 tools for any MCP-compatible AI client; OAuth + per-org consent) +- MCP Server: `https://mcp.cpln.io/mcp` (95 tools for any MCP-compatible AI client; OAuth + per-org consent) - Docs: https://docs.controlplane.com (page index for AI agents: https://docs.controlplane.com/llms.txt) - Full CLI conventions and hallucination traps: https://docs.controlplane.com/public/cli-conventions.md @@ -56,18 +56,18 @@ Org (Organization) — top-level isolation boundary, globally unique name ## Platform capabilities -| Capability | What it is | When to use | -| --- | --- | --- | -| **Workloads** | Deploy containers as serverless, standard, cron, or stateful | Primary deployment unit — most users start here | -| **Template Catalog** | 30+ production-ready templates (Postgres, Redis, Kafka, MongoDB, etc.) | Need a database, queue, or common service — install instead of building from scratch | -| **Managed Kubernetes (mk8s)** | Provision Kubernetes clusters across AWS, GCP, Azure, Hetzner, and more | Need a full Kubernetes cluster (teams deploy INTO mk8s clusters) | -| **CPLN BYOK** | Register existing Kubernetes clusters as Control Plane locations | Already have Kubernetes — want Control Plane workload management on top | -| **Kubernetes Operator** | Manage Control Plane resources as Kubernetes CRDs (ArgoCD/GitOps) | Want Kubernetes-native GitOps for Control Plane infrastructure | -| **Agents** | Secure tunnels to private networks (VPCs, on-prem, data centers) | Workloads need to reach private TCP endpoints behind firewalls | -| **External Logging** | Ship logs to S3, CloudWatch, Coralogix, Datadog, Logz.io, Stackdriver | Compliance, long-term retention, or external log analysis | -| **Domains** | Custom domain routing with auto-TLS, geo-routing, path-based routing | Expose workloads on your own domain with CNAME or NS delegation | -| **AI Plugin** | Plugin for Claude Code, Codex, and Antigravity CLI — bundles the MCP Server plus skills, agents, slash commands, and guardrails | Recommended path for AI-assisted infrastructure management when your tool supports plugins | -| **MCP Server** | Over 100 tools for AI agents to manage infrastructure programmatically, including job tools that do a whole task in one call; OAuth + per-org consent | Any MCP-compatible AI client (Cursor, Claude Web, Claude Desktop, ChatGPT, VS Code, Antigravity, Amp, OpenCode) | +| Capability | What it is | When to use | +| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | +| **Workloads** | Deploy containers as serverless, standard, cron, or stateful | Primary deployment unit — most users start here | +| **Template Catalog** | 30+ production-ready templates (Postgres, Redis, Kafka, MongoDB, etc.) | Need a database, queue, or common service — install instead of building from scratch | +| **Managed Kubernetes (mk8s)** | Provision Kubernetes clusters across AWS, GCP, Azure, Hetzner, and more | Need a full Kubernetes cluster (teams deploy INTO mk8s clusters) | +| **CPLN BYOK** | Register existing Kubernetes clusters as Control Plane locations | Already have Kubernetes — want Control Plane workload management on top | +| **Kubernetes Operator** | Manage Control Plane resources as Kubernetes CRDs (ArgoCD/GitOps) | Want Kubernetes-native GitOps for Control Plane infrastructure | +| **Agents** | Secure tunnels to private networks (VPCs, on-prem, data centers) | Workloads need to reach private TCP endpoints behind firewalls | +| **External Logging** | Ship logs to S3, CloudWatch, Coralogix, Datadog, Logz.io, Stackdriver | Compliance, long-term retention, or external log analysis | +| **Domains** | Custom domain routing with auto-TLS, geo-routing, path-based routing | Expose workloads on your own domain with CNAME or NS delegation | +| **AI Plugin** | Plugin for Claude Code, Codex, and Antigravity CLI — bundles the MCP Server plus skills, agents, slash commands, and guardrails | Recommended path for AI-assisted infrastructure management when your tool supports plugins | +| **MCP Server** | 95 tools for AI agents to manage infrastructure programmatically, including job tools that do a whole task in one call; OAuth + per-org consent | Any MCP-compatible AI client (Cursor, Claude Web, Claude Desktop, ChatGPT, VS Code, Antigravity, Amp, OpenCode) | ## Guardrails — read these first @@ -81,7 +81,7 @@ Context is established only when: the user named it in the current request, name > Before I run this, I want to confirm the target. Your active profile appears to be `` (org: ``, GVC: ``). Should I use that, or a different org / profile / GVC? -For **read-only** commands (`get`, `query`, `audit`, `logs`, `permissions`, `access-report`, `eventlog`), defaulting is acceptable — but **announce the target first**: *"Using profile `` → org ``, GVC ``…"* +For **read-only** commands (`get`, `query`, `audit`, `logs`, `permissions`, `access-report`, `eventlog`), defaulting is acceptable — but **announce the target first**: _"Using profile `` → org ``, GVC ``…"_ ### 2. Secret access — 3 mandatory steps @@ -111,18 +111,18 @@ A workload without firewall config cannot reach its database, be reached by user ### 5. Workload type constraints (immutable after creation) -| Feature | serverless | standard | stateful | cron | -| --- | --- | --- | --- | --- | -| Scale to zero | `rps` or `concurrency` | KEDA only | KEDA only | No | -| Ports | Exactly 1 container × 1 port (required) | 0 or more | 0 or more | Must NOT expose any | -| Capacity AI | Yes (default) | Yes (default) | Yes (API default off; Terraform and Pulumi default on) | Yes (default), applied at the next run | -| Persistent volumes | No | No | Yes (volume sets) | No | -| `replicaDirect` LB | No | No | **Only this type** | No | -| `spec.job` | Forbidden | Forbidden | Forbidden | Required | -| Multi-metric autoscaling | No | Yes (cpu/memory/rps) | Yes (cpu/memory/rps) | N/A | -| `maxConcurrency` | Used | Ignored | Ignored | N/A | -| `timeoutSeconds` max | 600 | 3600 | 3600 | N/A | -| Max containers per workload | 8 | 8 | 8 | 8 | +| Feature | serverless | standard | stateful | cron | +| --------------------------- | --------------------------------------- | -------------------- | ------------------------------------------------------ | -------------------------------------- | +| Scale to zero | `rps` or `concurrency` | KEDA only | KEDA only | No | +| Ports | Exactly 1 container × 1 port (required) | 0 or more | 0 or more | Must NOT expose any | +| Capacity AI | Yes (default) | Yes (default) | Yes (API default off; Terraform and Pulumi default on) | Yes (default), applied at the next run | +| Persistent volumes | No | No | Yes (volume sets) | No | +| `replicaDirect` LB | No | No | **Only this type** | No | +| `spec.job` | Forbidden | Forbidden | Forbidden | Required | +| Multi-metric autoscaling | No | Yes (cpu/memory/rps) | Yes (cpu/memory/rps) | N/A | +| `maxConcurrency` | Used | Ignored | Ignored | N/A | +| `timeoutSeconds` max | 600 | 3600 | 3600 | N/A | +| Max containers per workload | 8 | 8 | 8 | 8 | - **Workload type is immutable.** Changing type requires delete + recreate. Capture state first: `cpln workload get NAME --gvc GVC -o yaml-slim > NAME.bak.yaml`. - **Capacity AI** works on every workload type (on by default for Standard, Serverless, and Cron; Stateful and VM default off through the API but on through Terraform and Pulumi). It is incompatible with: CPU autoscaling, multi-metric autoscaling, GPUs. @@ -138,7 +138,7 @@ Before any destructive operation, present a structured summary AND wait for expl - **Service-disrupting**: `policy remove-binding` (breaks runtime access), `serviceaccount remove-key` (breaks CI/CD), `group remove-member` (locks users out), `gvc remove-location` (forces redeployment). - **Implicit destructive (immutability traps)**: org delete impossible; workload type/name immutable (rename via `cpln workload clone OLD --name NEW --gvc GVC`); volume set `fileSystemType` and `performanceClass` immutable; `cpln apply` of a renamed resource creates a new one (old must be deleted with `cpln delete --file ...`). -When delete + recreate is the only path: (1) capture state with `-o yaml-slim`, (2) reuse the same name to preserve URL/internal DNS/domain routes/policy targetLinks/identity bindings, (3) confirm — the user authorized the *goal*, not the *technique*. +When delete + recreate is the only path: (1) capture state with `-o yaml-slim`, (2) reuse the same name to preserve URL/internal DNS/domain routes/policy targetLinks/identity bindings, (3) confirm — the user authorized the _goal_, not the _technique_. **Required confirmation shape:** @@ -194,48 +194,48 @@ Stateful provisioning, large image pushes, mk8s creation, GVC location adds — ## Workload types — choosing one -| Need | Use | -| --- | --- | -| Web app that should scale to zero when idle | `serverless` | -| Service that must always be running, multiple ports, or non-HTTP | `standard` | -| Background job on a schedule | `cron` | -| Database or stateful app needing persistent storage | `stateful` | +| Need | Use | +| ---------------------------------------------------------------- | ------------ | +| Web app that should scale to zero when idle | `serverless` | +| Service that must always be running, multiple ports, or non-HTTP | `standard` | +| Background job on a schedule | `cron` | +| Database or stateful app needing persistent storage | `stateful` | ## Autoscaling strategies -| Strategy | Serverless | Standard | Stateful | Cron | -| --- | :-: | :-: | :-: | :-: | -| Concurrent Requests | Yes | No | No | No | -| Requests Per Second | Yes | Yes | Yes | No | -| CPU Utilization | Yes | Yes | Yes | No | -| Memory Utilization | Yes | Yes | Yes | No | -| Request Latency | No | Yes | Yes | No | -| Multi-metric (cpu/memory/rps) | No | Yes | Yes | No | -| KEDA (custom metrics) | No | Yes | Yes | No | +| Strategy | Serverless | Standard | Stateful | Cron | +| ----------------------------- | :--------: | :------: | :------: | :--: | +| Concurrent Requests | Yes | No | No | No | +| Requests Per Second | Yes | Yes | Yes | No | +| CPU Utilization | Yes | Yes | Yes | No | +| Memory Utilization | Yes | Yes | Yes | No | +| Request Latency | No | Yes | Yes | No | +| Multi-metric (cpu/memory/rps) | No | Yes | Yes | No | +| KEDA (custom metrics) | No | Yes | Yes | No | **Scale to zero**: Serverless supports it with any of its metrics. Standard and Stateful reach it only via KEDA. Cron and VM cannot scale to zero; they require minScale ≥ 1. -| Strategy | Best for | -| --- | --- | -| Concurrent Requests | Request-driven services, APIs with variable concurrency | -| Requests Per Second | High-throughput APIs with predictable patterns | -| CPU Utilization | Compute-heavy workloads (image processing, ML inference) | -| Memory Utilization | Memory-intensive applications (caches, data processing) | -| Request Latency | Latency-sensitive services (real-time, interactive) | -| KEDA | Event-driven scaling (queue depth, external metrics) | -| Disabled | Fixed replica count, manually managed scaling | +| Strategy | Best for | +| ------------------- | -------------------------------------------------------- | +| Concurrent Requests | Request-driven services, APIs with variable concurrency | +| Requests Per Second | High-throughput APIs with predictable patterns | +| CPU Utilization | Compute-heavy workloads (image processing, ML inference) | +| Memory Utilization | Memory-intensive applications (caches, data processing) | +| Request Latency | Latency-sensitive services (real-time, interactive) | +| KEDA | Event-driven scaling (queue depth, external metrics) | +| Disabled | Fixed replica count, manually managed scaling | ## Workload resource validation — must-know constraints -| Constraint | Rule | -| --- | --- | -| CPU / memory minimum | 25 millicores / 32 MiB | -| Memory-to-CPU ratio | `memory(MiB) / cpu(millicores)` ≤ 8 (relax to 32 with tag `cpln/relaxMemoryToCpuRatio`) | -| `port` vs `ports` | Mutually exclusive on the same container; port numbers unique across all containers | -| Volumes | Max 15, unique paths, no path can be a parent of another | -| `target` ceiling | ≤ 100 with cpu/memory metrics; not allowed with KEDA | +| Constraint | Rule | +| ------------------------- | ---------------------------------------------------------------------------------------- | +| CPU / memory minimum | 25 millicores / 32 MiB | +| Memory-to-CPU ratio | `memory(MiB) / cpu(millicores)` ≤ 8 (relax to 32 with tag `cpln/relaxMemoryToCpuRatio`) | +| `port` vs `ports` | Mutually exclusive on the same container; port numbers unique across all containers | +| Volumes | Max 15, unique paths, no path can be a parent of another | +| `target` ceiling | ≤ 100 with cpu/memory metrics; not allowed with KEDA | | `metric`/`multi`/`target` | `metric` and `multi` are mutually exclusive; `target` is mutually exclusive with `multi` | -| `replicaDirect` LB | Stateful only | +| `replicaDirect` LB | Stateful only | **Defaults**: `type=serverless`, `cpu=50m`, `memory=128Mi`, `autoscaling.target=95`, `minScale=1`, `maxScale=5`, `scaleToZeroDelay=300s`, `terminationGracePeriodSeconds=90`, `firewallConfig.internal.inboundAllowType=none`. @@ -247,15 +247,15 @@ For full validation (Stateful min/max ratios, GPU constraints — nvidia t4/a10g Reference syntax in env vars / volumes: -| Type | Form | -| --- | --- | -| `opaque` | `cpln://secret/NAME.payload` | +| Type | Form | +| ------------ | ----------------------------------------------------------------------- | +| `opaque` | `cpln://secret/NAME.payload` | | `dictionary` | `cpln://secret/NAME.KEY` (one env var per key, or mount as a directory) | -| `userpass` | `cpln://secret/NAME.username` / `.password` | -| `tls` | `cpln://secret/NAME.cert` / `.key` | -| `keypair` | `cpln://secret/NAME.publicKey` / `.privateKey` | -| `aws` | `cpln://secret/NAME.accessKey` / `.secretKey` / `.roleArn` | -| `gcp` | `cpln://secret/NAME` (typically a volume mount — JSON file) | +| `userpass` | `cpln://secret/NAME.username` / `.password` | +| `tls` | `cpln://secret/NAME.cert` / `.key` | +| `keypair` | `cpln://secret/NAME.publicKey` / `.privateKey` | +| `aws` | `cpln://secret/NAME.accessKey` / `.secretKey` / `.roleArn` | +| `gcp` | `cpln://secret/NAME` (typically a volume mount — JSON file) | **Injection methods**: env var (`cpln://secret/NAME...`), volume mount (large/multi-file/GCP JSON), or **pull secret at GVC level** (`spec.pullSecretLinks` — `docker`, `ecr`, `gcp` types only). @@ -272,12 +272,12 @@ cpln profile update default --org my-org --gvc my-gvc # set default org/GV For CI/CD, set environment variables through your platform's secrets management (never `--token`, which leaks into logs): -| Variable | Purpose | -| --- | --- | -| `CPLN_TOKEN` | Service account key (`cpln serviceaccount add-key`) | -| `CPLN_ORG` | Default organization | -| `CPLN_GVC` | Default GVC | -| `CPLN_PROFILE` | Profile override | +| Variable | Purpose | +| ------------------------ | -------------------------------------------------------------------- | +| `CPLN_TOKEN` | Service account key (`cpln serviceaccount add-key`) | +| `CPLN_ORG` | Default organization | +| `CPLN_GVC` | Default GVC | +| `CPLN_PROFILE` | Profile override | | `CPLN_SKIP_UPDATE_CHECK` | Any non-empty value skips the daily new-version check and its notice | Override per command: `--org`, `--gvc`, `--profile`. @@ -301,30 +301,30 @@ Resource commands: `cpln [REF] [--flags]`. Standalone comman ### Resource command map -| Resource | Scope | CRUD | Non-standard subcommands | -| --- | --- | --- | --- | -| **workload** | gvc | Full | `connect`, `exec`, `run`, `cron` (get/run/start/stop), `replica` (get/stop), `force-redeployment`, `get-deployments`, `open`, `start`, `stop` | -| **gvc** | org | Full | `add-location`, `remove-location`, `delete-all-workloads` | -| **secret** | org | **No generic create** | 12 type-specific create commands, `reveal` | -| **policy** | org | Full | `add-binding`, `remove-binding` | -| **identity** | gvc | Full | — | -| **volumeset** | gvc | Full | `expand`, `shrink`, `snapshot` (create/delete/get/restore), `volume` (delete/get) | -| **domain** | org | Full (no clone) | — | -| **cloudaccount** | org | **No generic create** | `create-aws`, `create-azure`, `create-gcp`, `create-ngs` | -| **image** | org | No create | `build`, `copy`, `docker-login` | -| **agent** | org | Full (no clone) | `info`, `manifest`, `up` | -| **group** | org | Full | `add-member`, `remove-member` | -| **ipset** | org | Full | `add-location`, `remove-location`, `update-location` | -| **serviceaccount** | org | Full (no update) | `add-key`, `remove-key` | -| **mk8s** | org | **No create** | `dashboard`, `join`, `kubeconfig` | -| **user** | org | No create | `invite` | -| **org** | — | **No delete** (immutable) | — | -| **profile** | local | get, delete, update | `create`, `login`, `set-default`, `token` | -| **helm** | gvc | get | `install`, `upgrade`, `template`, `rollback`, `uninstall`, `list`, `history` | -| **stack** | gvc | — | `deploy`, `manifest`, `rm` | -| **location** | org | Partial | `install`, `uninstall` | -| **auditctx** | org | Full (no delete) | — | -| **quota** | org | get, edit, patch | — | +| Resource | Scope | CRUD | Non-standard subcommands | +| ------------------ | ----- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | +| **workload** | gvc | Full | `connect`, `exec`, `run`, `cron` (get/run/start/stop), `replica` (get/stop), `force-redeployment`, `get-deployments`, `open`, `start`, `stop` | +| **gvc** | org | Full | `add-location`, `remove-location`, `delete-all-workloads` | +| **secret** | org | **No generic create** | 12 type-specific create commands, `reveal` | +| **policy** | org | Full | `add-binding`, `remove-binding` | +| **identity** | gvc | Full | — | +| **volumeset** | gvc | Full | `expand`, `shrink`, `snapshot` (create/delete/get/restore), `volume` (delete/get) | +| **domain** | org | Full (no clone) | — | +| **cloudaccount** | org | **No generic create** | `create-aws`, `create-azure`, `create-gcp`, `create-ngs` | +| **image** | org | No create | `build`, `copy`, `docker-login` | +| **agent** | org | Full (no clone) | `info`, `manifest`, `up` | +| **group** | org | Full | `add-member`, `remove-member` | +| **ipset** | org | Full | `add-location`, `remove-location`, `update-location` | +| **serviceaccount** | org | Full (no update) | `add-key`, `remove-key` | +| **mk8s** | org | **No create** | `dashboard`, `join`, `kubeconfig` | +| **user** | org | No create | `invite` | +| **org** | — | **No delete** (immutable) | — | +| **profile** | local | get, delete, update | `create`, `login`, `set-default`, `token` | +| **helm** | gvc | get | `install`, `upgrade`, `template`, `rollback`, `uninstall`, `list`, `history` | +| **stack** | gvc | — | `deploy`, `manifest`, `rm` | +| **location** | org | Partial | `install`, `uninstall` | +| **auditctx** | org | Full (no delete) | — | +| **quota** | org | get, edit, patch | — | ### Standard CRUD verbs @@ -332,41 +332,41 @@ Resource commands: `cpln [REF] [--flags]`. Standalone comman ### Commands that DON'T exist -| Wrong | Correct | -| --- | --- | -| `cpln secret create` | `cpln secret create-opaque`, `create-aws`, etc. (12 type-specific variants) | -| `cpln list` | `cpln get` (no args = list all) | -| `cpln mk8s create` | `cpln apply --file mk8s-manifest.yaml` | -| `cpln logs --follow` | `cpln logs --tail` (or `-t` or `-f`) | -| `cpln workload log` | `cpln logs '{gvc="GVC", workload="WORKLOAD"}'` | -| `cpln cloudaccount create` | `cpln cloudaccount create-aws`, `create-azure`, `create-gcp`, `create-ngs` | -| `cpln apply` (no `--file`) | `cpln apply --file manifest.yaml` | -| `cpln workload update --identity X` | `cpln workload update REF --set spec.identityLink=//identity/X` | -| `cpln secret update --data '{}'` | `cpln secret edit REF` or `cpln apply --file ...` | -| `cpln gvc update --location LOC` | `cpln gvc update REF --set 'spec.staticPlacement.locationLinks+=//location/LOC'` | -| `cpln image push` / `cpln image pull` | `cpln image build --push` (build+push) or `docker push` after `cpln image docker-login` | -| `cpln image tag` for version tags | Use Docker version tags directly via `cpln image build --name NAME:TAG`. `cpln image tag` exists but manages **metadata** key=value tags only, NOT Docker version tags. | -| `cpln workload create --type stateful/cron` | `cpln apply --file workload.yaml` — CLI flags only support serverless/standard create | +| Wrong | Correct | +| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `cpln secret create` | `cpln secret create-opaque`, `create-aws`, etc. (12 type-specific variants) | +| `cpln list` | `cpln get` (no args = list all) | +| `cpln mk8s create` | `cpln apply --file mk8s-manifest.yaml` | +| `cpln logs --follow` | `cpln logs --tail` (or `-t` or `-f`) | +| `cpln workload log` | `cpln logs '{gvc="GVC", workload="WORKLOAD"}'` | +| `cpln cloudaccount create` | `cpln cloudaccount create-aws`, `create-azure`, `create-gcp`, `create-ngs` | +| `cpln apply` (no `--file`) | `cpln apply --file manifest.yaml` | +| `cpln workload update --identity X` | `cpln workload update REF --set spec.identityLink=//identity/X` | +| `cpln secret update --data '{}'` | `cpln secret edit REF` or `cpln apply --file ...` | +| `cpln gvc update --location LOC` | `cpln gvc update REF --set 'spec.staticPlacement.locationLinks+=//location/LOC'` | +| `cpln image push` / `cpln image pull` | `cpln image build --push` (build+push) or `docker push` after `cpln image docker-login` | +| `cpln image tag` for version tags | Use Docker version tags directly via `cpln image build --name NAME:TAG`. `cpln image tag` exists but manages **metadata** key=value tags only, NOT Docker version tags. | +| `cpln workload create --type stateful/cron` | `cpln apply --file workload.yaml` — CLI flags only support serverless/standard create | ### High-value command shapes The resource command map + Standard CRUD verbs cover most cases. These are the syntax-sensitive ones worth memorizing: -| Task | Command | -| --- | --- | -| Apply / delete bundle | `cpln apply --file ./manifests/ --gvc GVC --ready`  ·  `cpln delete --file manifest.yaml` | -| Create workload (cron/stateful via apply, not CLI flags) | `cpln workload create --name APP --gvc GVC --image IMAGE --port PORT` | -| Create opaque secret | `cpln secret create-opaque --name SECRET --file data.txt` (use `--file -` for stdin) | -| Bind permission | `cpln policy add-binding POLICY --permission reveal --identity //gvc/GVC/identity/ID` | -| Assign identity to workload | `cpln workload update WL --gvc GVC --set spec.identityLink=//identity/ID` | -| View logs | `cpln logs '{gvc="GVC", workload="WL"}' --org ORG --tail` | -| Exec in container (note `--` separator) | `cpln workload exec WL --gvc GVC -- COMMAND` | -| Port forward | `cpln port-forward WL 8080:8080 --gvc GVC` | -| Export for re-apply | `cpln workload get WL --gvc GVC -o yaml-slim > wl.yaml` | -| Rename (preferred over get/edit/apply) | `cpln workload clone OLD --name NEW --gvc GVC` | -| Build & push image | `cpln image build --name IMAGE:TAG --push` | -| Build & push without local Docker | `cpln image build --name IMAGE:TAG --remote` (add `--repo ` to build a GitHub/GitLab repo) | -| Convert K8s manifests | `cpln convert --file k8s-manifest.yaml` | +| Task | Command | +| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | +| Apply / delete bundle | `cpln apply --file ./manifests/ --gvc GVC --ready`  ·  `cpln delete --file manifest.yaml` | +| Create workload (cron/stateful via apply, not CLI flags) | `cpln workload create --name APP --gvc GVC --image IMAGE --port PORT` | +| Create opaque secret | `cpln secret create-opaque --name SECRET --file data.txt` (use `--file -` for stdin) | +| Bind permission | `cpln policy add-binding POLICY --permission reveal --identity //gvc/GVC/identity/ID` | +| Assign identity to workload | `cpln workload update WL --gvc GVC --set spec.identityLink=//identity/ID` | +| View logs | `cpln logs '{gvc="GVC", workload="WL"}' --org ORG --tail` | +| Exec in container (note `--` separator) | `cpln workload exec WL --gvc GVC -- COMMAND` | +| Port forward | `cpln port-forward WL 8080:8080 --gvc GVC` | +| Export for re-apply | `cpln workload get WL --gvc GVC -o yaml-slim > wl.yaml` | +| Rename (preferred over get/edit/apply) | `cpln workload clone OLD --name NEW --gvc GVC` | +| Build & push image | `cpln image build --name IMAGE:TAG --push` | +| Build & push without local Docker | `cpln image build --name IMAGE:TAG --remote` (add `--repo ` to build a GitHub/GitLab repo) | +| Convert K8s manifests | `cpln convert --file k8s-manifest.yaml` | ## Manifest patterns @@ -381,19 +381,19 @@ spec: type: serverless containers: - name: main - image: nginx:latest # exact ref, NO docker.io/ prefix + image: nginx:latest # exact ref, NO docker.io/ prefix port: 80 memory: 256Mi cpu: 250m env: - name: DB_PASSWORD - value: cpln://secret/db-password.payload # opaque → .payload + value: cpln://secret/db-password.payload # opaque → .payload defaultOptions: autoscaling: { minScale: 1, maxScale: 5, metric: rps, target: 100 } firewallConfig: external: - inboundAllowCIDR: ["0.0.0.0/0"] - outboundAllowCIDR: ["0.0.0.0/0"] + inboundAllowCIDR: ['0.0.0.0/0'] + outboundAllowCIDR: ['0.0.0.0/0'] --- kind: secret name: db-password @@ -402,11 +402,11 @@ data: { encoding: plain, payload: my-secret-value } --- kind: policy name: secret-access -targetKind: secret # singular, lowercase +targetKind: secret # singular, lowercase targetLinks: [//secret/db-password] bindings: - permissions: [reveal] - principalLinks: [//gvc/my-gvc/identity/my-app-identity] # GVC-scoped principal link + principalLinks: [//gvc/my-gvc/identity/my-app-identity] # GVC-scoped principal link --- kind: identity name: my-app-identity @@ -490,12 +490,12 @@ For Terraform / Pulumi / Kubernetes Operator deployment patterns, fetch the rele **Load balancers:** -| Type | Scope | Custom ports | Static IPs | Workload type | -| --- | --- | :-: | :-: | --- | -| Default (shared) | All workloads | No | No | All | -| Direct | Per-workload | Yes (TCP/UDP) | Via IP Sets | All | -| Dedicated (GVC) | Per-GVC | Yes | Via IP Sets | All | -| Replica Direct (`spec.loadBalancer.replicaDirect`) | Per-replica | Configurable | Via IP Sets | **Stateful only** | +| Type | Scope | Custom ports | Static IPs | Workload type | +| -------------------------------------------------- | ------------- | :-----------: | :---------: | ----------------- | +| Default (shared) | All workloads | No | No | All | +| Direct | Per-workload | Yes (TCP/UDP) | Via IP Sets | All | +| Dedicated (GVC) | Per-GVC | Yes | Via IP Sets | All | +| Replica Direct (`spec.loadBalancer.replicaDirect`) | Per-replica | Configurable | Via IP Sets | **Stateful only** | Dedicated LB on the GVC (`spec.loadBalancer.dedicated: true`) unlocks: domain ports outside 443/80, wildcard hostnames, `redirect.class.status5xx`/`status401` rules, and `tcp` protocol on domains. `trustedProxies`: `0` (source IP), `1` (last `X-Forwarded-For`), `2` (second-to-last). @@ -509,7 +509,7 @@ Bind principals to permissions on resources. Wrong target kind, link format, or - **Principal links**: user `//user/EMAIL`, service account `//serviceaccount/NAME`, group `//group/NAME`, **identity `//gvc/GVC/identity/NAME`** (NEVER `//identity/NAME` — silent ignore). - Max 50 bindings per policy, 200 principal links per binding. Permissions in each binding must be sorted alphabetically and unique. Built-in policies (`origin: builtin`) cannot be modified or deleted. -For full per-kind permissions table (and what `reveal`, `use`, `manage`, etc. actually grant), use `cpln policy permissions`, the MCP `get_permissions` tool, or fetch [/reference/policy](https://docs.controlplane.com/reference/policy). +For full per-kind permissions table (and what `reveal`, `use`, `manage`, etc. actually grant), use `cpln policy permissions`, the MCP `get_permissions` tool (`full` or `readonly` profile), or fetch [/reference/policy](https://docs.controlplane.com/reference/policy). ## Identities @@ -519,15 +519,15 @@ Provider sections each have XOR rules (e.g. AWS `roleName` ⊻ `policyRefs`; GCP ## Domains -| Field | Rule | -| --- | --- | -| `dnsMode` | `cname` or `ns`. **Apex domains MUST use `cname`** — NS doesn't support apex. | -| `certChallengeType` | `http01` or `dns01`. **NS mode requires `dns01`** (`http01` rejected). | -| `certificateGeneratorLink` | Custom ACME CA via a [certificate generator](https://docs.controlplane.com/reference/certificategenerator). **Must not** be combined with `certChallengeType` or `tls.serverCertificate`; an `http01` generator is rejected with `dnsMode: ns`, `gvcLink`, or `workloadLink`. A `dns01` generator with provider `cpln` (the default, `dns01: {}`) needs the `_acme-challenge` CNAME like built-in `dns01`; credentialed providers need none. | -| `gvcLink` ⊻ `workloadLink` ⊻ `ports.routes` | Mutually exclusive. `workloadLink` (replica-direct) is Stateful only. | -| `acceptAllHosts` ⊻ `acceptAllSubdomains` | Mutually exclusive; both require dedicated LB. | -| `ports[].protocol` | `http`, `http2` (default), `tcp` (`tcp` requires dedicated LB). | -| Route scope | All routes in a domain must target workloads in the **same GVC**. | +| Field | Rule | +| ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `dnsMode` | `cname` or `ns`. **Apex domains MUST use `cname`** — NS doesn't support apex. | +| `certChallengeType` | `http01` or `dns01`. **NS mode requires `dns01`** (`http01` rejected). | +| `certificateGeneratorLink` | Custom ACME CA via a [certificate generator](https://docs.controlplane.com/reference/certificategenerator). **Must not** be combined with `certChallengeType` or `tls.serverCertificate`; an `http01` generator is rejected with `dnsMode: ns`, `gvcLink`, or `workloadLink`. A `dns01` generator with provider `cpln` (the default, `dns01: {}`) needs the `_acme-challenge` CNAME like built-in `dns01`; credentialed providers need none. | +| `gvcLink` ⊻ `workloadLink` ⊻ `ports.routes` | Mutually exclusive. `workloadLink` (replica-direct) is Stateful only. | +| `acceptAllHosts` ⊻ `acceptAllSubdomains` | Mutually exclusive; both require dedicated LB. | +| `ports[].protocol` | `http`, `http2` (default), `tcp` (`tcp` requires dedicated LB). | +| Route scope | All routes in a domain must target workloads in the **same GVC**. | CNAME records → `cpln.app`. NS records → `ns1.cpln.cloud`, `ns2.cpln.cloud`, `ns1.cpln.live`, `ns2.cpln.live`. Auto-TLS = Let's Encrypt (or a linked certificate generator's CA), valid 90 days, auto-renewed every 60. Custom certs use the `keypair` secret type with PEM content. For CORS, header wildcards, route limits, and full route schema, fetch [/reference/domain](https://docs.controlplane.com/reference/domain). @@ -535,16 +535,16 @@ CNAME records → `cpln.app`. NS records → `ns1.cpln.cloud`, `ns2.cpln.cloud`, Stateful only. Mount via `cpln://volumeset/NAME` with `recoveryPolicy: retain` (default) or `recycle`. -| Filesystem | Access | Binding | Snapshots / shrink / delete / restore | -| --- | --- | --- | :-: | -| `ext4` | RWO | 1 stateful workload | Yes | -| `xfs` | RWO | 1 stateful workload | Yes | -| `shared` | RWX | Any number of workloads | **No** | +| Filesystem | Access | Binding | Snapshots / shrink / delete / restore | +| ---------- | ------ | ----------------------- | :-----------------------------------: | +| `ext4` | RWO | 1 stateful workload | Yes | +| `xfs` | RWO | 1 stateful workload | Yes | +| `shared` | RWX | Any number of workloads | **No** | Performance classes: `general-purpose-ssd` (min 10 GB), `high-throughput-ssd` (**min 200 GB**), `shared` (auto-set when `fileSystemType: shared`). Max capacity 65,536 GB for all. - **Both `fileSystemType` AND `performanceClass` are immutable.** Changing either = delete + recreate (data loss — guardrail #6). -- `shrinkVolume` destroys data; volumes can be expanded only once every 6 hours. +- `shrinkVolume` copies the data to a smaller volume (never below `initialCapacity` or the used bytes plus 5%) and keeps the old one if the copy fails; a volume can be expanded at most 4 times in a rolling 24 hours. - `copyVolume` replaces the destination volume and deletes its storage device; it needs `acknowledgeDestinationVolumeDeletion: true` and `exec.copyVolume` (not in `exec`). - `createFinalSnapshot: true` (default) auto-snapshots before any volume deletion — leave on. @@ -603,7 +603,7 @@ Before submitting: - [/guides/cpln-apply](https://docs.controlplane.com/guides/cpln-apply) — GitOps, multi-resource apply, the patience-windowed safety-net pattern - [/cli-reference/overview](https://docs.controlplane.com/cli-reference/overview) — all CLI commands and flags -- [/ai/overview](https://docs.controlplane.com/ai/overview), [/ai/mcp](https://docs.controlplane.com/ai/mcp), [/ai/plugin](https://docs.controlplane.com/ai/plugin): AI Tools overview (choose-your-path), MCP Server (over 100 tools, OAuth + per-org consent), and full AI plugin (skills + agents + commands + guardrails) +- [/ai/overview](https://docs.controlplane.com/ai/overview), [/ai/mcp](https://docs.controlplane.com/ai/mcp), [/ai/plugin](https://docs.controlplane.com/ai/plugin): AI Tools overview (choose-your-path), MCP Server (95 tools, OAuth + per-org consent), and full AI plugin (skills + agents + commands + guardrails) - [/template-catalog/overview](https://docs.controlplane.com/template-catalog/overview) — 30+ production-ready templates - [/mk8s/overview](https://docs.controlplane.com/mk8s/overview), [/byok/overview](https://docs.controlplane.com/byok/overview), [/core/kubernetes-operator](https://docs.controlplane.com/core/kubernetes-operator) - Terraform: [registry.terraform.io/providers/controlplane-com/cpln](https://registry.terraform.io/providers/controlplane-com/cpln/latest/docs)