From cpln
Configures CDN caching (Cloudflare, CloudFront) and Envoy-based rate limiting for Control Plane workloads. Useful when the user asks about CDN, edge caching, rate limiting, throttling, per-key limits, or DDoS protection.
How this skill is triggered — by the user, by Claude, or both
Slash command
/cpln:cdn-rate-limitingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> **Tool availability:** some MCP tools named here live in the `full` toolset profile — if one is not advertised on this connection, tell the user to reconnect the MCP server with `?toolsets=full` (or use the `cpln` CLI fallback). Reads and deletes work on every profile via the generic `list_resources` / `get_resource` / `delete_resource` tools.
Tool availability: some MCP tools named here live in the
fulltoolset profile — if one is not advertised on this connection, tell the user to reconnect the MCP server with?toolsets=full(or use thecplnCLI fallback). Reads and deletes work on every profile via the genericlist_resources/get_resource/delete_resourcetools.
Two edge concerns, both built from existing primitives — there is no CDN or rate-limit resource kind. A CDN is bring-your-own (Cloudflare / CloudFront) pointed at the workload's canonical endpoint; rate limiting is an Envoy ratelimit service you deploy, enabled per workload by cpln/rateLimit* tags. Assumes the workload primer (firewall deny-by-default, canonical URL rules, create-then-verify).
The pattern: the CDN proxies your domain and uses the workload's canonical endpoint as origin — read it from status.canonicalEndpoint or mcp__cpln__list_deployments, never construct it. A workload endpoint gives precise geo-routing and per-workload failover; a GVC endpoint serves one CDN route for many domains or wildcard subdomains, but keeps sending traffic to every location even when that location's workload is down.
mcp__cpln__create_secret_tls with the cert and key, TLS chain left empty (the origin cert is self-signed).mcp__cpln__create_domain (CNAME DNS mode), mcp__cpln__set_domain_tls with the secret as the custom server certificate, mcp__cpln__add_domain_route to the workload. The apex domain must be verified before configuring a subdomain.us-east-1 (CloudFront requires that region), DNS validation, covering your subdomain or wildcard.mcp__cpln__list_deployments); alternate domain name = your subdomain; attach the ACM cert.*.cloudfront.net name.With a CDN in front, restrict the workload firewall so only CDN traffic reaches it: set inboundAllowCIDR to the provider's published ranges (CloudFront IP list) via mcp__cpln__update_workload, and keep the list current. BYOK locations must also admit the ranges in the cluster's ingress security group — the CloudFront list is large, so raise the VPC quota for rules per security group to at least 530. Details: firewall-networking.
Tags on the target workload inject an Envoy rate-limit filter into its inbound sidecar: each request makes a gRPC check (1s timeout) against a ratelimit service you deploy (Envoy ratelimit + Redis); over-limit requests get HTTP 429.
One multi-resource manifest (no bundled-apply MCP tool — use the CLI):
cpln gvc create --name ratelimit --location LOCATION --org ORG # or mcp__cpln__create_gvc
cpln apply --file rate-limiting.yaml --org ORG --gvc ratelimit
The example manifest creates the ratelimit workload (envoyproxy/ratelimit), a redis workload, the ratelimit-config opaque secret (the rules), and the identity + policy for secret access. It assumes the GVC is named ratelimit (edit it if yours differs) and pins an older envoyproxy/ratelimit image tag — substitute a newer tag if desired.
As shipped, the manifest is a trial setup, not a production one: both workloads run minScale: 1 with spot: true. Because enforcement is fail-closed, the ratelimit stack is tier-1 infrastructure for every tagged workload — for production raise its minScale to 2+, set spot: false, and run the GVC in the same locations as the workloads it protects (every request pays the check's round trip). A Redis restart only resets counters; a ratelimit outage denies traffic.
Edit the ratelimit-config opaque secret (mcp__cpln__update_secret_opaque), Envoy ratelimit format; unit: second / minute / hour / day:
domain: cpln
descriptors:
- key: authorization
rate_limit:
unit: minute
requests_per_unit: 10
After editing, reload the config: cpln workload force-redeployment ratelimit --gvc ratelimit.
Set with mcp__cpln__update_workload (or at creation):
| Tag | Required | Default | Meaning |
|---|---|---|---|
cpln/rateLimitAddress | Yes | — | Canonical endpoint hostname of the ratelimit workload (no scheme prefix) — nothing happens without it |
cpln/rateLimitDescriptors | Effectively yes | authority | Comma-separated: authorization, host, path — the default matches none of them, so no limiting is applied until you set this |
cpln/rateLimitScheme | No | https | https dials the service over TLS with SNI |
cpln/rateLimitPort | No | 443 | Port of the ratelimit service |
cpln/rateLimitDomain | No | cpln | Must match domain in the config secret |
| Descriptor | Buckets per | HTTP header |
|---|---|---|
authorization | API key / token | Authorization |
host | domain | Host |
path | endpoint | :path |
There is no per-IP descriptor — these three are the only ones wired. For per-client-IP throttling use the CDN layer (e.g. Cloudflare rate-limiting rules); keep this stack for per-token and per-route limits. Don't invent a remote_address descriptor — it does nothing.
cpln/rateLimitDescriptors: authorization,path produces one compound key (an ordered tuple), not two independent limits. The config must nest in the same order as the tag's comma order:
domain: cpln
descriptors:
- key: authorization # first tag entry
descriptors:
- key: path # second tag entry — nested, not a sibling
rate_limit:
unit: minute
requests_per_unit: 60
This buckets per token-and-path pair (any values). Add value: under a key to pin one route or token (e.g. key: path, value: /api/search). Two genuinely independent limits are not expressible through the tags — the filter emits a single action tuple.
Anonymous-bypass warning: Envoy skips the rate-limit check entirely when a descriptor header is missing from the request (skip_if_absent defaults to false and the filter doesn't set it). With authorization in the list, requests without an Authorization header are never checked — including the sample 10/minute config above. Limit by path or host (always present) when anonymous traffic matters, or throttle it at the CDN.
failure_mode_deny: true and a 1s check timeout — if the ratelimit service is down, unreachable, or the address is wrong, inbound requests are denied, not passed through. Verify the ratelimit workload is Ready (mcp__cpln__list_deployments) before tagging the target, and treat a typo in cpln/rateLimitAddress as an outage.authority produces an empty action list — the filter runs but limits nothing.Traffic passes, in order: the CDN (absorbs and caches), then the firewall (inboundAllowCIDR = CDN IPs only, so nobody bypasses the CDN), then rate limiting (catches abuse that passes the CDN), then the workload.
429 — with the sample 10/minute rule, the 11th call returns it: for i in $(seq 1 11); do curl -s -o /dev/null -w "%{http_code}\n" -H "Authorization: test" https://CANONICAL_ENDPOINT/; done. After any rules edit, force-redeployment the ratelimit workload first.curl -I https://SUBDOMAIN shows the CDN's header (cf-cache-status on Cloudflare, x-cache on CloudFront).curl the canonical endpoint directly — after the firewall lock-down it must no longer answer from outside the CDN ranges.| Symptom | Likely cause |
|---|---|
| Every request denied | Ratelimit service down/unready, or cpln/rateLimitAddress wrong — enforcement is fail-closed |
| Requests never limited | cpln/rateLimitDescriptors unset (default matches nothing); domain mismatch between tag and config; rules edited without force-redeployment; misspelled tag (tags are unvalidated) |
| Cloudflare 526 / TLS errors | Domain TLS not using the origin-cert secret, or SSL mode not Full (strict) |
| Redirect loop behind Cloudflare | SSL mode is Flexible — switch to Full (strict) |
| Origin still reachable directly | inboundAllowCIDR not restricted to the CDN ranges |
mcp__cpln__update_workload — cpln/rateLimit* tags; inboundAllowCIDR lock-downmcp__cpln__update_secret_opaque — edit the ratelimit rules secretmcp__cpln__create_secret_tls / create_domain / set_domain_tls / add_domain_route — CDN domain wiringmcp__cpln__list_deployments — canonical endpoint + readiness checkscpln apply --file rate-limiting.yaml (bundled manifest), cpln workload force-redeployment (config reload)| Need | Skill |
|---|---|
| Workload types, defaults, canonical URL rules — start here | workload |
| Firewall rules, CIDR allow-lists | firewall-networking |
| TLS, probes, hardening | workload-security |
npx claudepluginhub controlplane-com/ai-plugin --plugin cplnBuilds accessible UIs with shadcn/ui components on Radix UI + Tailwind CSS, plus canvas visuals. For React apps (Next.js, Vite, Remix, Astro), design systems, responsive layouts, themes, dark mode, prototypes.